/** Find a common ancestor that two TLV type attributes share
*
- * @param a first TLV attribute.
- * @param b second TLV attribute.
- * @param is_ancestor Enforce a->b relationship (a is parent or ancestor of b).
+ * @param a first TLV attribute.
+ * @param b second TLV attribute.
+ * @param is_ancestor Enforce a->b relationship (a is parent or ancestor of b).
* @return
* - Common ancestor if one exists.
* - NULL if no common ancestor exists.
}
if (strcmp(test_type, "decode-tacacs") == 0) {
- vp_cursor_t cursor;
+ fr_cursor_t cursor;
RADIUS_PACKET *packet = talloc(NULL, RADIUS_PACKET);
if (strcmp(p + 14, "-") == 0) {
continue;
}
- fr_pair_cursor_init(&cursor, &packet->vps);
+ fr_cursor_init(&cursor, &packet->vps);
p = output;
- for (vp = fr_pair_cursor_first(&cursor); vp; vp = fr_pair_cursor_next(&cursor)) {
+ for (vp = fr_cursor_head(&cursor); vp; vp = fr_cursor_next(&cursor)) {
fr_pair_snprint(p, sizeof(output) - (p - output), vp);
p += strlen(p);
}
if (strcmp(test_type, "decode-tacacs") == 0) {
- vp_cursor_t cursor;
+ fr_cursor_t cursor;
RADIUS_PACKET *packet = talloc(NULL, RADIUS_PACKET);
if (strcmp(p + 14, "-") == 0) {
continue;
}
- fr_pair_cursor_init(&cursor, &packet->vps);
+ fr_cursor_init(&cursor, &packet->vps);
p = output;
- for (vp = fr_pair_cursor_first(&cursor); vp; vp = fr_pair_cursor_next(&cursor)) {
+ for (vp = fr_cursor_head(&cursor); vp; vp = fr_cursor_next(&cursor)) {
fr_pair_snprint(p, sizeof(output) - (p - output), vp);
p += strlen(p);
if (strncmp(test_type, "decode-pair", 11) == 0) {
fr_test_point_pair_decode_t *tp = NULL;
ssize_t dec_len = 0;
- vp_cursor_t cursor;
+ fr_cursor_t cursor;
void *decoder_ctx = NULL;
p += load_test_point_by_command((void **)&tp, test_type, 11, "tp_decode") + 1;
}
}
- fr_pair_cursor_init(&cursor, &head);
+ fr_cursor_init(&cursor, &head);
while (len > 0) {
dec_len = tp->func(tp_ctx, &cursor, attr, len, decoder_ctx);
if (dec_len < 0) {
*/
if (head) {
p = output;
- for (vp = fr_pair_cursor_first(&cursor);
+ for (vp = fr_cursor_head(&cursor);
vp;
- vp = fr_pair_cursor_next(&cursor)) {
+ vp = fr_cursor_next(&cursor)) {
fr_pair_snprint(p, sizeof(output) - (p - output), vp);
p += strlen(p);
if (strncmp(test_type, "encode-pair", 11) == 0) {
fr_test_point_pair_encode_t *tp = NULL;
ssize_t enc_len = 0;
- vp_cursor_t cursor;
+ fr_cursor_t cursor;
void *encoder_ctx = NULL;
p += load_test_point_by_command((void **)&tp, test_type, 11, "tp_encode") + 1;
}
attr = data;
- fr_pair_cursor_init(&cursor, &head);
- while ((vp = fr_pair_cursor_current(&cursor))) {
+ fr_cursor_init(&cursor, &head);
+ while ((vp = fr_cursor_current(&cursor))) {
enc_len = tp->func(attr, data + sizeof(data) - attr, &cursor, encoder_ctx);
if (enc_len < 0) {
char *out_p = output, *out_end = out_p + sizeof(output);
UNUSED void const *mod_inst, UNUSED void const *xlat_inst,
REQUEST *request, char const *fmt)
{
- vp_cursor_t cursor;
+ fr_cursor_t cursor;
fr_cursor_t src_cursor;
vp_tmpl_t *src;
VALUE_PAIR *vp, *head = NULL;
goto error;
}
- fr_pair_cursor_init(&cursor, &head);
+ fr_cursor_init(&cursor, &head);
for (vp = tmpl_cursor_init(NULL, &src_cursor, request, src);
vp;
uint8_t const *p = vp->vp_octets, *end = p + vp->vp_length;
ssize_t len;
VALUE_PAIR *vps = NULL;
- vp_cursor_t options_cursor;
+ fr_cursor_t options_cursor;
- fr_pair_cursor_init(&options_cursor, &vps);
+ fr_cursor_init(&options_cursor, &vps);
/*
* Loop over all the options data
*/
}
p += len;
}
- fr_pair_cursor_merge(&cursor, vps);
+ fr_cursor_head(&options_cursor);
+ fr_cursor_merge(&cursor, &options_cursor);
}
- for (vp = fr_pair_cursor_first(&cursor);
+ for (vp = fr_cursor_head(&cursor);
vp;
- vp = fr_pair_cursor_next(&cursor)) {
+ vp = fr_cursor_next(&cursor)) {
rdebug_pair(L_DBG_LVL_2, request, vp, "dhcp_options: ");
decoded++;
}
UNUSED void const *mod_inst, UNUSED void const *xlat_inst,
REQUEST *request, char const *fmt)
{
- vp_cursor_t cursor;
+ fr_cursor_t cursor;
VALUE_PAIR *vp;
uint8_t binbuf[255];
ssize_t len;
while (isspace((int) *fmt)) fmt++;
if ((radius_copy_vp(request, &vp, request, fmt) < 0) || !vp) return 0;
- fr_pair_cursor_init(&cursor, &vp);
+ fr_cursor_init(&cursor, &vp);
len = fr_dhcpv4_encode_option(binbuf, sizeof(binbuf), &cursor, NULL);
talloc_free(vp);
size_t total;
uint8_t *ptr, *end;
VALUE_PAIR const *vp;
- vp_cursor_t cursor;
+ fr_cursor_t cursor;
total = 0;
- for (vp = fr_pair_cursor_init(&cursor, &request->reply->vps);
+ for (vp = fr_cursor_init(&cursor, &request->reply->vps);
vp != NULL;
- vp = fr_pair_cursor_next(&cursor)) {
+ vp = fr_cursor_next(&cursor)) {
/*
* Skip things which shouldn't be in channel bindings.
*/
ptr += 4;
end = ptr + total;
- fr_pair_cursor_init(&cursor, &request->reply->vps);
- while ((vp = fr_pair_cursor_current(&cursor)) && (ptr < end)) {
+ fr_cursor_init(&cursor, &request->reply->vps);
+ while ((vp = fr_cursor_current(&cursor)) && (ptr < end)) {
/*
* Skip things which shouldn't be in channel bindings.
*/
if (vp->da->flags.encrypt != FLAG_ENCRYPT_NONE) {
next:
- fr_pair_cursor_next(&cursor);
+ fr_cursor_next(&cursor);
continue;
}
if (!vp->da->vendor && (vp->da->attr == FR_MESSAGE_AUTHENTICATOR)) goto next;
/* Add the channel binding attributes to the fake packet */
data_len = chbind_get_data(chbind->request, CHBIND_NSID_RADIUS, &attr_data);
if (data_len) {
- vp_cursor_t cursor;
+ fr_cursor_t cursor;
rad_assert(data_len <= talloc_array_length((uint8_t const *) chbind->request));
- fr_pair_cursor_init(&cursor, &fake->packet->vps);
+ fr_cursor_init(&cursor, &fake->packet->vps);
while (data_len > 0) {
fr_radius_ctx_t decoder_ctx = {
.root = fr_dict_root(fr_dict_internal)
* of 32 bits, and includes the Type/Length fields.
*/
-static ssize_t sim_decode_pair_internal(TALLOC_CTX *ctx, vp_cursor_t *cursor, fr_dict_attr_t const *parent,
+static ssize_t sim_decode_pair_internal(TALLOC_CTX *ctx, fr_cursor_t *cursor, fr_dict_attr_t const *parent,
uint8_t const *data, size_t data_len, void *decoder_ctx);
-static ssize_t sim_decode_pair_value(TALLOC_CTX *ctx, vp_cursor_t *cursor, fr_dict_attr_t const *parent,
+static ssize_t sim_decode_pair_value(TALLOC_CTX *ctx, fr_cursor_t *cursor, fr_dict_attr_t const *parent,
uint8_t const *data, size_t const attr_len, size_t const data_len,
void *decoder_ctx);
return len / element_len;
}
-static ssize_t sim_decode_array(TALLOC_CTX *ctx, vp_cursor_t *cursor,
+static ssize_t sim_decode_array(TALLOC_CTX *ctx, fr_cursor_t *cursor,
fr_dict_attr_t const *parent,
uint8_t const *data, size_t const attr_len, UNUSED size_t data_len,
void *decoder_ctx)
* - Length on success.
* - < 0 on malformed attribute.
*/
-static ssize_t sim_decode_tlv(TALLOC_CTX *ctx, vp_cursor_t *cursor,
+static ssize_t sim_decode_tlv(TALLOC_CTX *ctx, fr_cursor_t *cursor,
fr_dict_attr_t const *parent,
uint8_t const *data, size_t const attr_len, size_t data_len,
void *decoder_ctx)
ssize_t decr_len;
fr_dict_attr_t const *child;
VALUE_PAIR *head = NULL;
- vp_cursor_t tlv_cursor;
+ fr_cursor_t tlv_cursor;
ssize_t rcode;
if (data_len < 2) {
/*
* Record where we were in the list when packet_ctx function was called
*/
- fr_pair_cursor_init(&tlv_cursor, &head);
+ fr_cursor_init(&tlv_cursor, &head);
while ((size_t)(end - p) >= sizeof(uint32_t)) {
uint8_t sim_at = p[0];
size_t sim_at_len = ((size_t)p[1]) << 2;
if (rcode < 0) goto error;
p += sim_at_len;
}
- fr_pair_cursor_merge(cursor, head); /* Wind to the end of the new pairs */
+ fr_cursor_head(&tlv_cursor);
+ fr_cursor_tail(cursor);
+ fr_cursor_merge(cursor, &tlv_cursor); /* Wind to the end of the new pairs */
talloc_free(decr);
return attr_len;
* - Length on success.
* - -1 on failure.
*/
-static ssize_t sim_decode_pair_value(TALLOC_CTX *ctx, vp_cursor_t *cursor, fr_dict_attr_t const *parent,
+static ssize_t sim_decode_pair_value(TALLOC_CTX *ctx, fr_cursor_t *cursor, fr_dict_attr_t const *parent,
uint8_t const *data, size_t const attr_len, size_t const data_len,
void *decoder_ctx)
{
done:
vp->type = VT_DATA;
- fr_pair_cursor_append(cursor, vp);
+ fr_cursor_append(cursor, vp);
return attr_len;
}
* - The number of bytes parsed.
* - -1 on error.
*/
-static ssize_t sim_decode_pair_internal(TALLOC_CTX *ctx, vp_cursor_t *cursor, fr_dict_attr_t const *parent,
+static ssize_t sim_decode_pair_internal(TALLOC_CTX *ctx, fr_cursor_t *cursor, fr_dict_attr_t const *parent,
uint8_t const *data, size_t data_len, void *decoder_ctx)
{
uint8_t sim_at;
* - The number of bytes parsed.
* - -1 on error.
*/
-ssize_t fr_sim_decode_pair(TALLOC_CTX *ctx, vp_cursor_t *cursor,
+ssize_t fr_sim_decode_pair(TALLOC_CTX *ctx, fr_cursor_t *cursor,
uint8_t const *data, size_t data_len, void *decoder_ctx)
{
fr_sim_decode_ctx_t *packet_ctx = decoder_ctx;
* - 0 on success.
* - -1 on failure.
*/
-int fr_sim_decode(REQUEST *request, vp_cursor_t *decoded,
+int fr_sim_decode(REQUEST *request, fr_cursor_t *decoded,
uint8_t const *data, size_t data_len, fr_sim_decode_ctx_t *decoder_ctx)
{
ssize_t rcode;
* Move the cursor to the end, so we know if
* any additional attributes were added.
*/
- fr_pair_cursor_end(decoded);
+ fr_cursor_tail(decoded);
/*
* We need at least enough data for the subtype
if (rcode <= 0) {
RPEDEBUG("Failed decoding AT");
error:
- fr_pair_cursor_free(decoded); /* Free any attributes we added */
+ fr_cursor_free_list(decoded); /* Free any attributes we added */
return -1;
}
goto error;
}
vp->vp_uint32 = data[0];
- fr_pair_cursor_append(decoded, vp);
+ fr_cursor_append(decoded, vp);
}
return 0;
static ssize_t encode_tlv_hdr(uint8_t *out, size_t outlen,
fr_dict_attr_t const **tlv_stack, unsigned int depth,
- vp_cursor_t *cursor, void *encoder_ctx);
+ fr_cursor_t *cursor, void *encoder_ctx);
/** Find the next attribute to encode
*
* @param encoder_ctx the context for the encoder
* @return encodable VALUE_PAIR, or NULL if none available.
*/
-static inline VALUE_PAIR *next_encodable(vp_cursor_t *cursor, void *encoder_ctx)
+static inline VALUE_PAIR *next_encodable(fr_cursor_t *cursor, void *encoder_ctx)
{
VALUE_PAIR *vp;
fr_sim_encode_ctx_t *packet_ctx = encoder_ctx;
- for (;;) {
- vp = fr_pair_cursor_next_by_ancestor(cursor, packet_ctx->root, TAG_ANY);
- if (!vp || !vp->da->flags.internal) break;
+ while ((vp = fr_cursor_next(cursor))) {
+ if (vp->da->flags.internal) continue;
+ if (fr_dict_parent_common(packet_ctx->root, vp->da, true)) break;
}
- return fr_pair_cursor_current(cursor);
+ return fr_cursor_current(cursor);
}
/** Determine if the current attribute is encodable, or find the first one that is
* @param encoder_ctx the context for the encoder
* @return encodable VALUE_PAIR, or NULL if none available.
*/
-static inline VALUE_PAIR *first_encodable(vp_cursor_t *cursor, void *encoder_ctx)
+static inline VALUE_PAIR *first_encodable(fr_cursor_t *cursor, void *encoder_ctx)
{
VALUE_PAIR *vp;
fr_sim_encode_ctx_t *packet_ctx = encoder_ctx;
- vp = fr_pair_cursor_current(cursor);
- if (vp && !vp->da->flags.internal && fr_dict_parent_common(packet_ctx->root, vp->da, true)) {
- cursor->found = vp;
- return vp;
- }
+ vp = fr_cursor_current(cursor);
+ if (vp && !vp->da->flags.internal && fr_dict_parent_common(packet_ctx->root, vp->da, true)) return vp;
return next_encodable(cursor, encoder_ctx);
}
*/
static ssize_t encode_value(uint8_t *out, size_t outlen,
fr_dict_attr_t const **tlv_stack, int depth,
- vp_cursor_t *cursor, void *encoder_ctx)
+ fr_cursor_t *cursor, void *encoder_ctx)
{
ssize_t len;
- VALUE_PAIR const *vp = fr_pair_cursor_current(cursor);
+ VALUE_PAIR const *vp = fr_cursor_current(cursor);
fr_dict_attr_t const *da = tlv_stack[depth];
fr_sim_encode_ctx_t *packet_ctx = encoder_ctx;
if (2 > outlen) goto oos;
out[0] = 0;
out[1] = 0;
- len = 2;
+ len = 2; /* Length of the reserved area */
break;
/*
*/
static ssize_t encode_array(uint8_t *out, size_t outlen,
fr_dict_attr_t const **tlv_stack, int depth,
- vp_cursor_t *cursor, void *encoder_ctx)
+ fr_cursor_t *cursor, void *encoder_ctx)
{
uint8_t *p = out, *end = p + outlen;
uint8_t *value;
p += slen;
- vp = fr_pair_cursor_current(cursor);
+ vp = fr_cursor_current(cursor);
if (!vp || (vp->da != da)) break; /* Stop if we have an attribute of a different type */
}
* Otherwise, attribute may be something else.
*/
static ssize_t encode_rfc_hdr(uint8_t *out, size_t outlen, fr_dict_attr_t const **tlv_stack, unsigned int depth,
- vp_cursor_t *cursor, void *encoder_ctx)
+ fr_cursor_t *cursor, void *encoder_ctx)
{
size_t pad_len;
uint8_t *p = out, *end = p + outlen;
static inline ssize_t encode_tlv(uint8_t *out, size_t outlen,
fr_dict_attr_t const **tlv_stack, unsigned int depth,
- vp_cursor_t *cursor, void *encoder_ctx)
+ fr_cursor_t *cursor, void *encoder_ctx)
{
ssize_t slen;
uint8_t *p = out, *end = p + outlen, *value;
- VALUE_PAIR const *vp = fr_pair_cursor_current(cursor);
+ VALUE_PAIR const *vp = fr_cursor_current(cursor);
fr_dict_attr_t const *da = tlv_stack[depth];
if (outlen < 2) {
/*
* If nothing updated the attribute, stop
*/
- if (!fr_pair_cursor_current(cursor) || (vp == fr_pair_cursor_current(cursor))) break;
+ if (!fr_cursor_current(cursor) || (vp == fr_cursor_current(cursor))) break;
/*
* We can encode multiple sub TLVs, if after
* at this depth is the same.
*/
if (da != tlv_stack[depth]) break;
- vp = fr_pair_cursor_current(cursor);
+ vp = fr_cursor_current(cursor);
}
/*
static ssize_t encode_tlv_hdr(uint8_t *out, size_t outlen,
fr_dict_attr_t const **tlv_stack, unsigned int depth,
- vp_cursor_t *cursor, void *encoder_ctx)
+ fr_cursor_t *cursor, void *encoder_ctx)
{
unsigned int total_len;
ssize_t len;
uint8_t *p = out;
fr_dict_attr_t const *da;
- VP_VERIFY(fr_pair_cursor_current(cursor));
+ VP_VERIFY(fr_cursor_current(cursor));
FR_PROTO_STACK_PRINT(tlv_stack, depth);
if (tlv_stack[depth]->type != FR_TYPE_TLV) {
return p - out; /* AT_IV + AT_*(TLV) - Can't use total_len, doesn't include IV */
}
-ssize_t fr_sim_encode_pair(uint8_t *out, size_t outlen, vp_cursor_t *cursor, void *encoder_ctx)
+ssize_t fr_sim_encode_pair(uint8_t *out, size_t outlen, fr_cursor_t *cursor, void *encoder_ctx)
{
VALUE_PAIR const *vp;
int ret;
/*
* We couldn't do it, so we didn't do anything.
*/
- if (fr_pair_cursor_current(cursor) == vp) {
+ if (fr_cursor_current(cursor) == vp) {
fr_strerror_printf("%s: Nested attribute structure too large to encode", __FUNCTION__);
return -1;
}
bool do_hmac = false;
unsigned char subtype;
- vp_cursor_t cursor;
+ fr_cursor_t cursor;
fr_sim_encode_ctx_t *packet_ctx = encode_ctx;
eap_packet_t *eap_packet = packet_ctx->eap_packet;
* Group attributes with similar lineages together
*/
fr_pair_list_sort(&to_encode, fr_pair_cmp_by_parent_num_tag);
- (void)fr_pair_cursor_init(&cursor, &to_encode);
+ (void)fr_cursor_init(&cursor, &to_encode);
/*
* Fast path...
return 0;
}
- fr_pair_cursor_first(&cursor); /* Reset */
+ fr_cursor_head(&cursor); /* Reset */
MEM(p = buff = talloc_zero_array(eap_packet, uint8_t, 1024)); /* We'll shrink this later */
end = p + talloc_array_length(p);
/*
* Encode all the things...
*/
- (void)fr_pair_cursor_first(&cursor);
- while ((vp = fr_pair_cursor_current(&cursor))) {
+ (void)fr_cursor_head(&cursor);
+ while ((vp = fr_cursor_current(&cursor))) {
slen = fr_sim_encode_pair(p, end - p, &cursor, packet_ctx);
if (slen < 0) {
error:
/*
* decode.c
*/
-ssize_t fr_sim_decode_pair(TALLOC_CTX *ctx, vp_cursor_t *cursor,
+ssize_t fr_sim_decode_pair(TALLOC_CTX *ctx, fr_cursor_t *cursor,
uint8_t const *data, size_t data_len, void *decoder_ctx);
-int fr_sim_decode(REQUEST *request, vp_cursor_t *decoded,
+int fr_sim_decode(REQUEST *request, fr_cursor_t *decoded,
uint8_t const *data, size_t data_len, fr_sim_decode_ctx_t *ctx);
/*
* encode.c
*/
-ssize_t fr_sim_encode_pair(uint8_t *out, size_t outlen, vp_cursor_t *cursor, void *encoder_ctx);
+ssize_t fr_sim_encode_pair(uint8_t *out, size_t outlen, fr_cursor_t *cursor, void *encoder_ctx);
ssize_t fr_sim_encode(REQUEST *request, VALUE_PAIR *to_encode, void *encode_ctx);
static int eap_aka_compose(eap_session_t *eap_session)
{
eap_aka_session_t *eap_aka_session = talloc_get_type_abort(eap_session->opaque, eap_aka_session_t);
- vp_cursor_t cursor;
- vp_cursor_t to_encode;
+ fr_cursor_t cursor;
+ fr_cursor_t to_encode;
VALUE_PAIR *head = NULL, *vp;
REQUEST *request = eap_session->request;
ssize_t ret;
};
fr_dict_attr_t const *encr = fr_dict_attr_child_by_num(dict_sim_root, FR_EAP_AKA_ENCR_DATA);
- fr_pair_cursor_init(&cursor, &eap_session->request->reply->vps);
- fr_pair_cursor_init(&to_encode, &head);
+ fr_cursor_init(&cursor, &eap_session->request->reply->vps);
+ fr_cursor_init(&to_encode, &head);
- while ((fr_pair_cursor_next_by_ancestor(&cursor, dict_aka_root, TAG_ANY))) {
- vp = fr_pair_cursor_remove(&cursor);
+ while ((vp = fr_cursor_current(&cursor))) {
+ if (!fr_dict_parent_common(dict_aka_root, vp->da, true)) {
+ fr_cursor_next(&cursor);
+ continue;
+ }
+ vp = fr_cursor_remove(&cursor);
/*
* Silently discard encrypted attributes until
continue;
}
- fr_pair_cursor_append(&to_encode, vp);
+ fr_cursor_append(&to_encode, vp);
}
RDEBUG2("Encoding EAP-AKA attributes");
eap_session->this_round->set_request_id = true;
ret = fr_sim_encode(eap_session->request, head, &encoder_ctx);
- fr_pair_cursor_first(&to_encode);
- fr_pair_cursor_free(&to_encode);
+ fr_cursor_head(&to_encode);
+ fr_cursor_free_list(&to_encode);
if (ret < 0) {
RPEDEBUG("Failed encoding EAP-AKA data");
.root = dict_aka_root
};
VALUE_PAIR *vp, *vps, *subtype_vp;
- vp_cursor_t cursor;
+ fr_cursor_t cursor;
eap_aka_subtype_t subtype;
/* vps is the data from the client */
vps = request->packet->vps;
- fr_pair_cursor_init(&cursor, &request->packet->vps);
- fr_pair_cursor_last(&cursor);
+ fr_cursor_init(&cursor, &request->packet->vps);
+ fr_cursor_tail(&cursor);
ret = fr_sim_decode(eap_session->request,
&cursor,
return RLM_MODULE_HANDLED; /* We need to process more packets */
}
- vp = fr_pair_cursor_current(&cursor);
+ vp = fr_cursor_current(&cursor);
if (vp && RDEBUG_ENABLED2) {
RDEBUG2("EAP-AKA decoded attributes");
rdebug_pair_list(L_DBG_LVL_2, request, vp, NULL);
static int eap_sim_compose(eap_session_t *eap_session, uint8_t const *hmac_extra, size_t hmac_extra_len)
{
eap_sim_session_t *eap_sim_session = talloc_get_type_abort(eap_session->opaque, eap_sim_session_t);
- vp_cursor_t cursor;
- vp_cursor_t to_encode;
+ fr_cursor_t cursor;
+ fr_cursor_t to_encode;
VALUE_PAIR *head = NULL, *vp;
REQUEST *request = eap_session->request;
fr_sim_encode_ctx_t encoder_ctx = {
/* we will set the ID on requests, since we have to HMAC it */
eap_session->this_round->set_request_id = true;
- fr_pair_cursor_init(&cursor, &eap_session->request->reply->vps);
- fr_pair_cursor_init(&to_encode, &head);
+ fr_cursor_init(&cursor, &eap_session->request->reply->vps);
+ fr_cursor_init(&to_encode, &head);
- while ((fr_pair_cursor_next_by_ancestor(&cursor, dict_sim_root, TAG_ANY))) {
- vp = fr_pair_cursor_remove(&cursor);
+ while ((vp = fr_cursor_current(&cursor))) {
+ if (!fr_dict_parent_common(dict_sim_root, vp->da, true)) {
+ fr_cursor_next(&cursor);
+ continue;
+ }
+ vp = fr_cursor_remove(&cursor);
/*
* Silently discard encrypted attributes until
continue;
}
- fr_pair_cursor_append(&to_encode, vp);
+ fr_cursor_append(&to_encode, vp);
}
RDEBUG2("Encoding EAP-SIM attributes");
eap_session->this_round->set_request_id = true;
ret = fr_sim_encode(eap_session->request, head, &encoder_ctx);
- fr_pair_cursor_first(&to_encode);
- fr_pair_cursor_free(&to_encode);
+ fr_cursor_head(&to_encode);
+ fr_cursor_free_list(&to_encode);
if (ret < 0) {
RPEDEBUG("Failed encoding EAP-SIM data");
.root = dict_sim_root
};
VALUE_PAIR *subtype_vp, *from_peer, *vp;
- vp_cursor_t cursor;
+ fr_cursor_t cursor;
eap_sim_subtype_t subtype;
*/
from_peer = eap_session->request->packet->vps;
- fr_pair_cursor_init(&cursor, &request->packet->vps);
- fr_pair_cursor_last(&cursor);
+ fr_cursor_init(&cursor, &request->packet->vps);
+ fr_cursor_tail(&cursor);
ret = fr_sim_decode(eap_session->request,
&cursor,
return RLM_MODULE_HANDLED; /* We need to process more packets */
}
- vp = fr_pair_cursor_current(&cursor);
+ vp = fr_cursor_current(&cursor);
if (vp && RDEBUG_ENABLED2) {
RDEBUG2("Decoded EAP-SIM attributes");
rdebug_pair_list(L_DBG_LVL_2, request, vp, NULL);
#include <freeradius-devel/dhcpv4/dhcpv4.h>
#include <freeradius-devel/io/test_point.h>
-static ssize_t decode_tlv(TALLOC_CTX *ctx, vp_cursor_t *cursor, fr_dict_attr_t const *parent,
+static ssize_t decode_tlv(TALLOC_CTX *ctx, fr_cursor_t *cursor, fr_dict_attr_t const *parent,
uint8_t const *data, size_t data_len);
-static ssize_t decode_value(TALLOC_CTX *ctx, vp_cursor_t *cursor,
+static ssize_t decode_value(TALLOC_CTX *ctx, fr_cursor_t *cursor,
fr_dict_attr_t const *parent, uint8_t const *data, size_t data_len);
/** Returns the number of array members for arrays with fixed element sizes
/*
* Decode ONE value into a VP
*/
-static ssize_t decode_value_internal(TALLOC_CTX *ctx, vp_cursor_t *cursor, fr_dict_attr_t const *da,
+static ssize_t decode_value_internal(TALLOC_CTX *ctx, fr_cursor_t *cursor, fr_dict_attr_t const *da,
uint8_t const *data, size_t data_len)
{
VALUE_PAIR *vp;
/* Need another VP for the next round */
if (p < end) {
- fr_pair_cursor_append(cursor, vp);
+ fr_cursor_append(cursor, vp);
vp = fr_pair_afrom_da(ctx, da);
if (!vp) return -1;
finish:
FR_PROTO_TRACE("decoding value complete, adding new pair and returning %zu byte(s)", p - data);
- fr_pair_cursor_append(cursor, vp);
+ fr_cursor_append(cursor, vp);
return p - data;
}
* @param[in] data to parse.
* @param[in] data_len of data parsed.
*/
-static ssize_t decode_tlv(TALLOC_CTX *ctx, vp_cursor_t *cursor, fr_dict_attr_t const *parent,
+static ssize_t decode_tlv(TALLOC_CTX *ctx, fr_cursor_t *cursor, fr_dict_attr_t const *parent,
uint8_t const *data, size_t data_len)
{
uint8_t const *p = data;
return p - data;
}
-static ssize_t decode_value(TALLOC_CTX *ctx, vp_cursor_t *cursor,
+static ssize_t decode_value(TALLOC_CTX *ctx, fr_cursor_t *cursor,
fr_dict_attr_t const *parent, uint8_t const *data, size_t data_len)
{
unsigned int values, i; /* How many values we need to decode */
* @param[in] data_len of data to parse.
* @param[in] decoder_ctx Unused.
*/
-ssize_t fr_dhcpv4_decode_option(TALLOC_CTX *ctx, vp_cursor_t *cursor,
+ssize_t fr_dhcpv4_decode_option(TALLOC_CTX *ctx, fr_cursor_t *cursor,
uint8_t const *data, size_t data_len, void *decoder_ctx)
{
ssize_t ret;
/*
* decode.c
*/
-ssize_t fr_dhcpv4_decode_option(TALLOC_CTX *ctx, vp_cursor_t *cursor,
+ssize_t fr_dhcpv4_decode_option(TALLOC_CTX *ctx, fr_cursor_t *cursor,
uint8_t const *data, size_t len, void *decoder_ctx);
/*
* encode.c
*/
ssize_t fr_dhcpv4_encode_option(uint8_t *out, size_t outlen,
- vp_cursor_t *cursor, void *encoder_ctx);
+ fr_cursor_t *cursor, void *encoder_ctx);
/*
* packet.c
*/
static ssize_t encode_value(uint8_t *out, size_t outlen,
fr_dict_attr_t const **tlv_stack, unsigned int depth,
- vp_cursor_t *cursor)
+ fr_cursor_t *cursor)
{
uint32_t lvalue;
- VALUE_PAIR *vp = fr_pair_cursor_current(cursor);
+ VALUE_PAIR *vp = fr_cursor_current(cursor);
uint8_t *p = out;
FR_PROTO_STACK_PRINT(tlv_stack, depth);
default:
fr_strerror_printf("Unsupported option type %d", vp->vp_type);
- (void)fr_pair_cursor_next(cursor);
+ (void)fr_cursor_next(cursor);
return -2;
}
- vp = fr_pair_cursor_next(cursor); /* We encoded a leaf, advance the cursor */
+ vp = fr_cursor_next(cursor); /* We encoded a leaf, advance the cursor */
fr_proto_tlv_stack_build(tlv_stack, vp ? vp->da : NULL);
FR_PROTO_STACK_PRINT(tlv_stack, depth);
* - < 0 on error.
*/
static ssize_t encode_rfc_hdr(uint8_t *out, ssize_t outlen,
- fr_dict_attr_t const **tlv_stack, unsigned int depth, vp_cursor_t *cursor)
+ fr_dict_attr_t const **tlv_stack, unsigned int depth, fr_cursor_t *cursor)
{
ssize_t len;
uint8_t *p = out;
fr_dict_attr_t const *da = tlv_stack[depth];
- VALUE_PAIR *vp = fr_pair_cursor_current(cursor);
+ VALUE_PAIR *vp = fr_cursor_current(cursor);
if (outlen < 3) return 0; /* No space */
FR_PROTO_TRACE("%zu byte(s) available in option", outlen - out[1]);
- next = fr_pair_cursor_current(cursor);
+ next = fr_cursor_current(cursor);
if (!next || (vp->da != next->da)) break;
vp = next;
} while (vp->da->flags.array);
* - < 0 on error.
*/
static ssize_t encode_tlv_hdr(uint8_t *out, ssize_t outlen,
- fr_dict_attr_t const **tlv_stack, unsigned int depth, vp_cursor_t *cursor)
+ fr_dict_attr_t const **tlv_stack, unsigned int depth, fr_cursor_t *cursor)
{
ssize_t len;
uint8_t *p = out;
- VALUE_PAIR const *vp = fr_pair_cursor_current(cursor);
+ VALUE_PAIR const *vp = fr_cursor_current(cursor);
fr_dict_attr_t const *da = tlv_stack[depth];
if (outlen < 5) return 0; /* No space */
/*
* If nothing updated the attribute, stop
*/
- if (!fr_pair_cursor_current(cursor) || (vp == fr_pair_cursor_current(cursor))) break;
+ if (!fr_cursor_current(cursor) || (vp == fr_cursor_current(cursor))) break;
/*
* We can encode multiple sub TLVs, if after
* at this depth is the same.
*/
if (da != tlv_stack[depth]) break;
- vp = fr_pair_cursor_current(cursor);
+ vp = fr_cursor_current(cursor);
}
return p - out;
* - < 0 error.
* - 0 not valid option for DHCP (skipping).
*/
-ssize_t fr_dhcpv4_encode_option(uint8_t *out, size_t outlen, vp_cursor_t *cursor, UNUSED void *encoder_ctx)
+ssize_t fr_dhcpv4_encode_option(uint8_t *out, size_t outlen, fr_cursor_t *cursor, UNUSED void *encoder_ctx)
{
VALUE_PAIR *vp;
unsigned int depth = 0;
fr_dict_attr_t const *tlv_stack[FR_DICT_MAX_TLV_STACK + 1];
ssize_t len;
- vp = fr_pair_cursor_current(cursor);
+ vp = fr_cursor_current(cursor);
if (!vp) return -1;
if (vp->da->vendor != DHCP_MAGIC_VENDOR) goto next; /* not a DHCP option */
if ((vp->da->attr > 255) && (DHCP_BASE_ATTR(vp->da->attr) != FR_DHCPV4_OPTION_82)) {
next:
fr_strerror_printf("Attribute \"%s\" is not a DHCP option", vp->da->name);
- fr_pair_cursor_next(cursor);
+ fr_cursor_next(cursor);
return 0;
}
size_t i;
uint8_t *p;
uint32_t giaddr;
- vp_cursor_t cursor;
+ fr_cursor_t cursor;
VALUE_PAIR *head = NULL, *vp;
VALUE_PAIR *maxms, *mtu;
- fr_pair_cursor_init(&cursor, &head);
+ fr_cursor_init(&cursor, &head);
p = packet->data;
if (packet->data[1] > 1) {
if (!vp) continue;
- fr_pair_cursor_append(&cursor, vp);
+ fr_cursor_append(&cursor, vp);
}
/*
* it'll need to find the new tail...
*/
{
- uint8_t const *end;
- ssize_t len;
+ uint8_t const *end;
+ ssize_t len;
fr_dhcp_decoder_ctx_t packet_ctx = {
- .root = fr_dict_root(fr_dict_internal)
- };
+ .root = fr_dict_root(fr_dict_internal)
+ };
p = packet->data + 240;
end = p + (packet->data_len - 240);
int fr_dhcpv4_packet_encode(RADIUS_PACKET *packet)
{
uint8_t *p;
- vp_cursor_t cursor;
+ fr_cursor_t cursor;
VALUE_PAIR *vp;
uint32_t lvalue;
uint16_t svalue;
* operates correctly. This changes the order of the list, but never mind...
*/
fr_pair_list_sort(&packet->vps, fr_dhcpv4_attr_cmp);
- fr_pair_cursor_init(&cursor, &packet->vps);
+ fr_cursor_init(&cursor, &packet->vps);
/*
* Each call to fr_dhcpv4_encode_option will encode one complete DHCP option,
* and sub options.
*/
- while ((vp = fr_pair_cursor_current(&cursor))) {
+ while ((vp = fr_cursor_current(&cursor))) {
len = fr_dhcpv4_encode_option(p, packet->data_len - (p - packet->data), &cursor, NULL);
if (len < 0) break;
p += len;
--- /dev/null
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/**
+ * $Id$
+ *
+ * @file protocols/dhcpv6/encode.c
+ * @brief Functions to encode DHCP options.
+ *
+ * @author Arran Cudbard-Bell <a.cudbardb@freeradius.org>
+ *
+ * @copyright 2018 The FreeRADIUS server project
+ * @copyright 2018 NetworkRADIUS SARL <info@networkradius.com>
+ */
+#include <stdint.h>
+#include <stddef.h>
+#include <talloc.h>
+#include <freeradius-devel/pair.h>
+#include <freeradius-devel/types.h>
+#include <freeradius-devel/proto.h>
+
+#include "dhcpv6.h"
+
+size_t const fr_dhcpv6_attr_sizes[FR_TYPE_MAX + 1][2] = {
+ [FR_TYPE_INVALID] = {~0, 0}, //!< Ensure array starts at 0 (umm?)
+
+ [FR_TYPE_STRING] = {0, ~0},
+ [FR_TYPE_OCTETS] = {0, ~0},
+
+ [FR_TYPE_IPV4_ADDR] = {4, 4},
+ [FR_TYPE_IPV4_PREFIX] = {1, 5}, //!< Zero length prefix still requires one byte for prefix len.
+ [FR_TYPE_IPV6_ADDR] = {16, 16},
+ [FR_TYPE_IPV6_PREFIX] = {1, 17}, //!< Zero length prefix still requires one byte for prefix len.
+ [FR_TYPE_IFID] = {8, 8},
+ [FR_TYPE_ETHERNET] = {6, 6},
+
+ [FR_TYPE_BOOL] = {1, 1},
+ [FR_TYPE_UINT8] = {1, 1},
+ [FR_TYPE_UINT16] = {2, 2},
+ [FR_TYPE_UINT32] = {4, 4},
+ [FR_TYPE_UINT64] = {8, 8},
+
+ [FR_TYPE_TLV] = {2, ~0},
+ [FR_TYPE_STRUCT] = {1, ~0},
+
+ [FR_TYPE_MAX] = {~0, 0} //!< Ensure array covers all types.
+};
+
+/** Return the on-the-wire length of an attribute value
+ *
+ * @param[in] vp to return the length of.
+ * @return the length of the attribute.
+ */
+size_t fr_dhcpv6_option_len(VALUE_PAIR const *vp)
+{
+ switch (vp->vp_type) {
+ case FR_TYPE_VARIABLE_SIZE:
+ if (da->flags.length) return da->flags.length; /* Variable type with fixed length */
+ return vp->vp_length;
+
+ default:
+ return fr_dhcpv6_attr_sizes[vp->vp_type][0];
+
+ case FR_TYPE_STRUCTURAL:
+ if (!fr_cond_assert(0)) return 0;
+ }
+}
*/
RCSIDH(dhcpv6_h, "$Id$")
+extern size_t const fr_dhcpv6_attr_sizes[FR_TYPE_MAX + 1][2];
+
+#define OPT_HDR_LEN (sizeof(uint16_t) * 2)
+
+typedef struct {
+ fr_dict_attr_t const *root; //!< Root attribute of the dictionary.
+} fr_dhcpv6_encode_ctx_t;
+
+/*
+ * base.c
+ */
+size_t fr_dhcpv6_option_len(VALUE_PAIR const *vp);
+
+/*
+ * encode.c
+ */
ssize_t fr_dhcpv6_encode_option(uint8_t *out, size_t outlen, fr_cursor_t *cursor, void *encoder_ctx);
+/*
+ * decode.c
+ */
ssize_t fr_dhcpv6_decode_option(TALLOC_CTX *ctx, vp_cursor_t *cursor,
uint8_t const *data, size_t data_len, void *decoder_ctx);
#endif
*
* @author Arran Cudbard-Bell <a.cudbardb@freeradius.org>
*
- * @copyright 2018 The FreeRADIUS server project
+ * @copyright 2018 The freeradius server project
* @copyright 2018 NetworkRADIUS SARL <info@networkradius.com>
*/
#include <stdint.h>
#include "dhcpv6.h"
-static ssize_t encode_tlv_hdr(UNUSED uint8_t *out, UNUSED size_t outlen,
- UNUSED fr_dict_attr_t const *tlv_stack[], UNUSED unsigned int depth, UNUSED fr_cursor_t *cursor)
+static ssize_t encode_tlv_hdr(uint8_t *out, size_t outlen,
+ fr_dict_attr_t const **tlv_stack, unsigned int depth,
+ fr_cursor_t *cursor, void *encoder_ctx);
+
+/** Find the next attribute to encode
+ *
+ * @param[in] cursor to iterate over.
+ * @param[in] encoder_ctx the context for the encoder.
+ * @return
+ * - An encodable VALUE_PAIR.
+ * - NULL if no encodable VALUE_PAIRs are available.
+ */
+static inline VALUE_PAIR *next_encodable(fr_cursor_t *cursor, void *encoder_ctx)
+{
+ VALUE_PAIR *vp;
+ fr_dhcpv6_encode_ctx_t *packet_ctx = encoder_ctx;
+
+ while ((vp = fr_cursor_next(cursor))) {
+ if (vp->da->flags.internal) continue;
+ if (fr_dict_parent_common(packet_ctx->root, vp->da, true)) break;
+ }
+
+ return vp;
+}
+
+/** Determine if the current attribute is encodable, or find the first one that is
+ *
+ * @param[in] cursor to iterate over.
+ * @param[in] encoder_ctx the context for the encoder.
+ * @return
+ * - An encodable VALUE_PAIR.
+ * - NULL if no encodable VALUE_PAIRs are available.
+ */
+/*
+static inline VALUE_PAIR *first_encodable(fr_cursor_t *cursor, void *encoder_ctx)
{
- return 0;
+ VALUE_PAIR *vp;
+ fr_dhcpv6_encode_ctx_t *packet_ctx = encoder_ctx;
+
+ vp = fr_cursor_current(cursor);
+ if (vp && !vp->da->flags.internal && fr_dict_parent_common(packet_ctx->root, vp->da, true)) return vp;
+
+ return next_encodable(cursor, encoder_ctx);
}
+*/
-static ssize_t encode_rfc_hdr(UNUSED uint8_t *out, UNUSED size_t outlen,
- UNUSED fr_dict_attr_t const *tlv_stack[], UNUSED unsigned int depth, UNUSED fr_cursor_t *cursor)
+/** Macro-like function for encoding an option header
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | option-code | option-len |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * @param[out] out Where to write the 4 byte option header.
+ * @param[in] outlen Length of the output buffer.
+ * @param[in] option The option number (host byte order).
+ * @param[in] data_len The length of the option (host byte order).
+ * @return
+ * - <0 How much data would have been required as a negative value.
+ * - 4 The length of data written.
+ */
+static inline ssize_t encode_option_hdr(uint8_t *out, size_t outlen, uint16_t option, size_t data_len)
{
- return 0;
+ uint16_t opt, len;
+ uint8_t *p = out;
+
+ CHECK_FREESPACE(outlen, OPT_HDR_LEN);
+
+ opt = htons(option);
+ len = htons(data_len);
+
+ memcpy(p, &opt, sizeof(opt));
+ p += sizeof(opt);
+ memcpy(p, &len, sizeof(len));
+
+ return p - out;
+}
+
+static ssize_t encode_value(uint8_t *out, size_t outlen,
+ fr_dict_attr_t const **tlv_stack, unsigned int depth,
+ fr_cursor_t *cursor, void *encoder_ctx)
+{
+ ssize_t slen;
+ uint8_t *p = out, *end = p + outlen;
+ VALUE_PAIR const *vp = fr_cursor_current(cursor);
+ fr_dict_attr_t const *da = tlv_stack[depth];
+
+ VP_VERIFY(vp);
+ FR_PROTO_STACK_PRINT(tlv_stack, depth);
+
+ /*
+ * Pack multiple attributes into into a single option
+ */
+// if (da->type == FR_TYPE_STRUCT) {
+// len = encode_struct(out, outlen, tlv_stack, depth, cursor, encoder_ctx);
+// if (len < 0) return len;
+//
+// vp = next_encodable(cursor);
+// fr_proto_tlv_stack_build(tlv_stack, vp ? vp->da : NULL);
+// return len;
+// }
+
+ /*
+ * If it's not a TLV, it should be a value type RFC
+ * attribute make sure that it is.
+ */
+ if (tlv_stack[depth + 1] != NULL) {
+ fr_strerror_printf("%s: Encoding value but not at top of stack", __FUNCTION__);
+ return PAIR_ENCODE_ERROR;
+ }
+
+ if (vp->da != da) {
+ fr_strerror_printf("%s: Top of stack does not match vp->da", __FUNCTION__);
+ return PAIR_ENCODE_ERROR;
+ }
+
+ switch (da->type) {
+ case FR_TYPE_STRUCTURAL:
+ fr_strerror_printf("%s: Called with structural type %s", __FUNCTION__,
+ fr_int2str(dict_attr_types, tlv_stack[depth]->type, "?Unknown?"));
+ return PAIR_ENCODE_ERROR;
+
+ default:
+ break;
+ }
+
+
+ switch (da->type) {
+ /*
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | option-code | option-len |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * . String .
+ * | ... |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+ case FR_TYPE_OCTETS:
+ case FR_TYPE_STRING:
+ /*
+ * If asked to encode more data than allowed,
+ * we encode only the allowed data.
+ */
+ slen = fr_dhcpv6_option_len(vp);
+ CHECK_FREESPACE(outlen, slen);
+
+ if (vp->vp_length < (size_t)slen) {
+ memcpy(p, vp->vp_ptr, vp->vp_length);
+ memset(p + vp->vp_length, 0, slen - vp->vp_length);
+ } else {
+ memcpy(p, vp->vp_ptr, vp->vp_length);
+ }
+ p += slen;
+ break;
+
+ /*
+ * Common encoder might add scope byte, so we just copy the address portion
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | option-code | option-len |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * | ipv6-address |
+ * | |
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+ case FR_TYPE_IPV6_ADDR:
+ CHECK_FREESPACE(outlen, sizeof(vp->vp_ipv6addr));
+
+ memcpy(out, vp->vp_ipv6addr, sizeof(vp->vp_ipv6addr));
+ p += sizeof(vp->vp_ipv6addr);
+ break;
+
+ /*
+ * Common encoder doesn't add a reserved byte after prefix, but it also
+ * doesn't do the variable length encoding required.
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | option-code | option-length |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | prefix6len | ipv6-prefix |
+ * +-+-+-+-+-+-+-+-+ (variable length) |
+ * . .
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+ case FR_TYPE_IPV6_PREFIX:
+ {
+ uint8_t prefix_len;
+
+ prefix_len = vp->vp_ip.prefix >> 3; /* Convert bits to whole bytes */
+ CHECK_FREESPACE(outlen, prefix_len + 1);
+
+ *p++ = vp->vp_ip.prefix;
+ memcpy(p, &vp->vp_ipv6addr, prefix_len); /* Only copy the minimum address bytes required */
+ p += prefix_len;
+ }
+ break;
+
+ /*
+ * Not actually specified by the DHCPv6 RFC, but will probably come
+ * in handy at some point if we need to have the DHCPv6 server
+ * hand out v4 prefixes.
+ */
+ case FR_TYPE_IPV4_PREFIX:
+ {
+ uint8_t prefix_len;
+
+ prefix_len = vp->vp_ip.prefix >> 3; /* Convert bits to whole bytes */
+ CHECK_FREESPACE(outlen, prefix_len + 1);
+
+ *p++ = vp->vp_ip.prefix;
+ memcpy(p, &vp->vp_ipv4addr, prefix_len); /* Only copy the minimum address bytes required */
+ p += prefix_len;
+ }
+ break;
+
+ /*
+ * Normal data types use the common encoder.
+ */
+ case FR_TYPE_IPV4_ADDR:
+ case FR_TYPE_IFID:
+ case FR_TYPE_ETHERNET: /* just in case */
+
+ /*
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | option-code | option-len |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+ case FR_TYPE_BOOL:
+ return 0; /* Bools are always true if present, so this is an empty option */
+
+ /*
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | option-code | option-len |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 8-bit-integer |
+ * +-+-+-+-+-+-+-+-+
+ */
+ case FR_TYPE_UINT8:
+
+ /*
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | option-code | option-len |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 16-bit-integer |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+ case FR_TYPE_UINT16:
+ /*
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | option-code | option-len |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 32-bit-integer |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+ case FR_TYPE_UINT32:
+ case FR_TYPE_UINT64:
+ case FR_TYPE_INT8:
+ case FR_TYPE_INT16:
+ case FR_TYPE_INT32:
+ case FR_TYPE_INT64:
+ case FR_TYPE_DATE:
+ CHECK_FREESPACE(outlen, fr_dhcpv6_option_len(vp));
+ slen = fr_value_box_to_network(NULL, p, end - p, &vp->data);
+ if (slen < 0) return PAIR_ENCODE_ERROR;
+ p += slen;
+ break;
+
+ case FR_TYPE_INVALID:
+ case FR_TYPE_EXTENDED:
+ case FR_TYPE_LONG_EXTENDED:
+ case FR_TYPE_COMBO_IP_ADDR: /* Should have been converted to concrete equivalent */
+ case FR_TYPE_COMBO_IP_PREFIX: /* Should have been converted to concrete equivalent */
+ case FR_TYPE_EVS:
+ case FR_TYPE_VSA:
+ case FR_TYPE_VENDOR:
+ case FR_TYPE_TLV:
+ case FR_TYPE_STRUCT:
+ case FR_TYPE_SIZE:
+ case FR_TYPE_TIMEVAL:
+ case FR_TYPE_ABINARY:
+ case FR_TYPE_FLOAT32:
+ case FR_TYPE_FLOAT64:
+ case FR_TYPE_DATE_MILLISECONDS:
+ case FR_TYPE_DATE_MICROSECONDS:
+ case FR_TYPE_DATE_NANOSECONDS:
+ case FR_TYPE_VALUE_BOX:
+ case FR_TYPE_MAX:
+ fr_strerror_printf("Unsupported attribute type %d", da->type);
+ return PAIR_ENCODE_ERROR;
+ }
+
+ /*
+ * Rebuilds the TLV stack for encoding the next attribute
+ */
+ vp = next_encodable(cursor, encoder_ctx);
+ fr_proto_tlv_stack_build(tlv_stack, vp ? vp->da : NULL);
+
+ return p - out;
+}
+
+static ssize_t encode_array(uint8_t *out, size_t outlen,
+ fr_dict_attr_t const **tlv_stack, int depth,
+ fr_cursor_t *cursor, void *encoder_ctx)
+{
+ uint8_t *p = out, *end = p + outlen;
+ ssize_t slen;
+ size_t element_len;
+ fr_dict_attr_t const *da = tlv_stack[depth];
+
+ if (!fr_cond_assert_msg(da->flags.array,
+ "%s: Internal sanity check failed, attribute \"%s\" does not have array bit set",
+ __FUNCTION__, da->name)) return PAIR_ENCODE_ERROR;
+
+ while (p < end) {
+ uint16_t *len_field;
+ VALUE_PAIR *vp;
+
+ element_len = fr_dhcpv6_option_len(fr_cursor_current(cursor));
+
+ /*
+ * If the data is variable length i.e. strings or octets
+ * we need to include a length field before each element.
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-...-+-+-+-+-+-+-+
+ * | text-len | String |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-...-+-+-+-+-+-+-+
+ */
+ if (!da->flags.length) {
+ CHECK_FREESPACE(sizeof(uint16_t) + element_len, end - p);
+ len_field = (uint16_t *)p;
+ p += sizeof(uint16_t); /* Make room for the length field */
+ }
+
+ slen = encode_value(p, end - p, tlv_stack, depth, cursor, encoder_ctx);
+ if (slen < 0) return slen;
+ if (!fr_cond_assert(slen < UINT16_MAX)) return PAIR_ENCODE_ERROR;
+
+ /*
+ * Ensure we always create elements of the correct length.
+ * This is mainly for fixed length octets type attributes
+ * containing one or more keys.
+ */
+ if (da->flags.length) {
+ if ((size_t)slen < element_len) {
+ memset(p + slen, 0, element_len - slen);
+ slen = element_len;
+ } else if ((size_t)slen > element_len){
+ slen = element_len;
+ }
+ }
+
+ p += slen;
+
+ /*
+ * Populate the length field
+ */
+ if (!da->flags.length) *len_field = htons((uint16_t) slen);
+
+ vp = fr_cursor_current(cursor);
+ if (!vp || (vp->da != da)) break; /* Stop if we have an attribute of a different type */
+ }
+
+ return p - out;
+}
+
+/** Encode an RFC format TLV.
+ *
+ * This could be a standard attribute, or a TLV data type.
+ * If it's a standard attribute, then vp->da->attr == attribute.
+ * Otherwise, attribute may be something else.
+ */
+static ssize_t encode_rfc_hdr(uint8_t *out, size_t outlen,
+ fr_dict_attr_t const **tlv_stack, unsigned int depth,
+ fr_cursor_t *cursor, void *encoder_ctx)
+{
+ uint8_t *p = out, *end = p + outlen;
+ ssize_t slen;
+ fr_dict_attr_t const *da;
+
+ FR_PROTO_STACK_PRINT(tlv_stack, depth);
+
+ da = tlv_stack[depth];
+
+ switch (da->type) {
+ case FR_TYPE_STRUCTURAL:
+ fr_strerror_printf("%s: Called with structural type %s", __FUNCTION__,
+ fr_int2str(dict_attr_types, da->type, "?Unknown?"));
+ return PAIR_ENCODE_ERROR;
+
+ default:
+ if (((da->vendor == 0) && (da->attr == 0)) || (da->attr > UINT16_MAX)) {
+ fr_strerror_printf("%s: Called with non-standard attribute %u", __FUNCTION__,
+ tlv_stack[depth]->attr);
+ return PAIR_ENCODE_ERROR;
+ }
+ break;
+ }
+
+ CHECK_FREESPACE(outlen, OPT_HDR_LEN);
+
+ /*
+ * Make space for the header...
+ */
+ p += OPT_HDR_LEN;
+
+ /*
+ * Write out the option's value
+ */
+ if (da->flags.array) {
+ slen = encode_array(p, end - p, tlv_stack, depth, cursor, encoder_ctx);
+ } else {
+ slen = encode_value(p, end - p, tlv_stack, depth, cursor, encoder_ctx);
+ }
+ if (slen < 0) return slen;
+ p += slen;
+
+ /*
+ * Write out the option number and length (before the value we jus wrote)
+ */
+ slen = encode_option_hdr(out, outlen, (uint16_t)tlv_stack[depth]->attr, (uint16_t)slen);
+ if (slen < 0) return slen;
+
+#ifndef NDEBUG
+ FR_PROTO_HEX_DUMP("Done RFC header", out, p - out);
+#endif
+
+ return p - out;
+}
+
+static inline ssize_t encode_tlv_internal(uint8_t *out, size_t outlen,
+ fr_dict_attr_t const **tlv_stack, unsigned int depth,
+ fr_cursor_t *cursor, void *encoder_ctx)
+{
+ ssize_t slen;
+ uint8_t *p = out, *end = p + outlen, *value;
+ VALUE_PAIR const *vp = fr_cursor_current(cursor);
+ fr_dict_attr_t const *da = tlv_stack[depth];
+
+ CHECK_FREESPACE(outlen, OPT_HDR_LEN);
+
+ while ((size_t)(end - p) > OPT_HDR_LEN) {
+ FR_PROTO_STACK_PRINT(tlv_stack, depth);
+
+ /*
+ * Determine the nested type and call the appropriate encoder
+ */
+ if (tlv_stack[depth + 1]->type == FR_TYPE_TLV) {
+ slen = encode_tlv_hdr(p, end - p, tlv_stack, depth + 1, cursor, encoder_ctx);
+ } else {
+ slen = encode_rfc_hdr(p, end - p, tlv_stack, depth + 1, cursor, encoder_ctx);
+ }
+ if (slen < 0) return slen;
+
+ p += slen;
+
+ /*
+ * If nothing updated the attribute, stop
+ */
+ if (!fr_cursor_current(cursor) || (vp == fr_cursor_current(cursor))) break;
+
+ /*
+ * We can encode multiple sub TLVs, if after
+ * rebuilding the TLV Stack, the attribute
+ * at this depth is the same.
+ */
+ if (da != tlv_stack[depth]) break;
+ vp = fr_cursor_current(cursor);
+ }
+
+#ifndef NDEBUG
+ FR_PROTO_HEX_DUMP("Done TLV body", out, p - out);
+#endif
+
+ return p - out;
+}
+
+static ssize_t encode_tlv_hdr(uint8_t *out, size_t outlen,
+ fr_dict_attr_t const **tlv_stack, unsigned int depth,
+ fr_cursor_t *cursor, void *encoder_ctx)
+{
+ ssize_t slen;
+ uint8_t *p = out, *end = p + outlen;
+ fr_dict_attr_t const *da;
+
+ VP_VERIFY(fr_cursor_current(cursor));
+ FR_PROTO_STACK_PRINT(tlv_stack, depth);
+
+ if (tlv_stack[depth]->type != FR_TYPE_TLV) {
+ fr_strerror_printf("%s: Expected type \"tlv\" got \"%s\"", __FUNCTION__,
+ fr_int2str(dict_attr_types, tlv_stack[depth]->type, "?Unknown?"));
+ return PAIR_ENCODE_ERROR;
+ }
+
+ if (!tlv_stack[depth + 1]) {
+ fr_strerror_printf("%s: Can't encode empty TLV", __FUNCTION__);
+ return PAIR_ENCODE_ERROR;
+ }
+
+ CHECK_FREESPACE(outlen, OPT_HDR_LEN);
+
+ da = tlv_stack[depth]; /* Remember this, stack might change */
+
+ p += OPT_HDR_LEN; /* Make room for option header */
+ slen = encode_tlv_internal(p, end - p, tlv_stack, depth, cursor, encoder_ctx);
+ if (slen < 0) return slen;
+ p += slen;
+
+ /*
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | option-code | option-len |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+ slen = encode_option_hdr(out, outlen, (uint16_t)da->attr, (uint16_t)slen);
+ if (slen < 0) return slen;
+
+#ifndef NDEBUG
+ FR_PROTO_HEX_DUMP("Done TLV header", out, p - out);
+#endif
+
+ return p - out;
+}
+
+/** Encode a VSIO (Vendor Specific Information Opion)
+ *
+ * If it's in the RFC format, call encode_rfc_hdr. Otherwise, encode it here.
+ * This allows variable length vendor options. There is no specific format
+ * specified for vendor option data, so we need to allow for variable width
+ * option fields and length field widths.
+ *
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * . .
+ * . option-data .
+ * . .
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+static ssize_t encode_vendor_attr_hdr(uint8_t *out, size_t outlen,
+ fr_dict_attr_t const **tlv_stack, unsigned int depth,
+ fr_cursor_t *cursor, void *encoder_ctx)
+{
+ ssize_t slen;
+ uint8_t *p = out, *end = p + outlen, *len_field;
+ size_t hdr_len;
+ fr_dict_attr_t const *da, *dv;
+
+ FR_PROTO_STACK_PRINT(tlv_stack, depth);
+
+ /*
+ * This is the dictionary attribute which contains the
+ * vendor IANA ID.
+ */
+ dv = tlv_stack[depth++];
+ if (dv->type != FR_TYPE_VENDOR) {
+ fr_strerror_printf("Expected Vendor");
+ return PAIR_ENCODE_ERROR;
+ }
+
+ da = tlv_stack[depth];
+
+ /*
+ * If the option field size is 1 byte, we can just
+ * encode it as a standard option header.
+ */
+ if ((da->type != FR_TYPE_TLV) &&
+ (dv->flags.type_size == 1) &&
+ (dv->flags.length == 1)) return encode_rfc_hdr(out, outlen, tlv_stack, depth, cursor, encoder_ctx);
+
+ hdr_len = dv->flags.type_size + dv->flags.length;
+ CHECK_FREESPACE(end - p, hdr_len);
+
+ /*
+ * Vendors use different widths for their option
+ * number fields.
+ */
+ switch (dv->flags.type_size) {
+ default:
+ fr_strerror_printf("%s: Internal sanity check failed, type %u", __FUNCTION__,
+ (unsigned) dv->flags.type_size);
+ return PAIR_ENCODE_ERROR;
+
+ case 4: /* 32bit */
+ *p++ = (da->attr >> 24) & 0xff;
+ *p++ = (da->attr >> 16) & 0xff;
+ *p++ = (da->attr >> 8) & 0xff;
+ *p++ = (da->attr & 0xff);
+ break;
+
+ case 3: /* 24bit */
+ *p++ = (da->attr >> 16) & 0xff;
+ *p++ = (da->attr >> 8) & 0xff;
+ *p++ = (da->attr & 0xff);
+ break;
+
+ case 2: /* 16bit */
+ *p++ = (da->attr >> 8) & 0xff;
+ *p++ = (da->attr & 0xff);
+ break;
+
+ case 1: /* 8 bit */
+ *p++ = (da->attr & 0xff);
+ break;
+ }
+
+ switch (dv->flags.length) {
+ default:
+ fr_strerror_printf("%s: Internal sanity check failed, length %u",
+ __FUNCTION__, (unsigned) dv->flags.length);
+ return PAIR_ENCODE_ERROR;
+
+ case 0: /* No length field ??? */
+ break;
+
+ case 2: /* 16bit length field */
+ len_field = p;
+ p += sizeof(uint16_t);
+ break;
+
+ case 1: /* 8bit length field */
+ len_field = p;
+ p += sizeof(uint8_t);
+ break;
+ }
+
+ /*
+ * Because we've now encoded the attribute header,
+ * if this is a TLV, we must process it via the
+ * internal tlv function, else we get a double TLV header.
+ */
+ if (da->type == FR_TYPE_TLV) {
+ slen = encode_tlv_internal(p, end - p, tlv_stack, depth, cursor, encoder_ctx);
+ /*
+ * Array of values inside a vendor option
+ */
+ } else if (da->flags.array) {
+ slen = encode_array(p, end - p, tlv_stack, depth, cursor, encoder_ctx);
+ /*
+ * Normal vendor option
+ */
+ } else {
+ slen = encode_value(p, end - p, tlv_stack, depth, cursor, encoder_ctx);
+ }
+ if (slen < 0) return slen;
+ p += slen;
+
+ switch (dv->flags.length) {
+ default:
+ break;
+
+ case 2:
+ len_field[0] = ((end - p) >> 8) & 0xff;
+ len_field[1] = (end - p) & 0xff;
+ break;
+
+ case 1:
+ len_field[0] = (end - p) & 0xff;
+ break;
+ }
+
+#ifndef NDEBUG
+ FR_PROTO_HEX_DUMP("Done VSIO body", out, end - p);
+#endif
+
+ return p - out;
+}
+
+/** Encode a Vendor-Specific Information Option
+ *
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | OPTION_VENDOR_OPTS | option-len |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | enterprise-number |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * . .
+ * . option-data .
+ * . .
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+static ssize_t encode_vsio_hdr(uint8_t *out, size_t outlen,
+ fr_dict_attr_t const **tlv_stack, unsigned int depth,
+ fr_cursor_t *cursor, void *encoder_ctx)
+{
+ ssize_t slen;
+ uint32_t pen;
+ uint8_t *p = out, *end = p + outlen;
+ fr_dict_attr_t const *da = tlv_stack[depth];
+
+ FR_PROTO_STACK_PRINT(tlv_stack, depth);
+
+ /*
+ * DA should be a VSA type with the value of OPTION_VENDOR_OPTS.
+ */
+ if (da->type != FR_TYPE_VSA) {
+ fr_strerror_printf("%s: Expected type \"vsa\" got \"%s\"", __FUNCTION__,
+ fr_int2str(dict_attr_types, da->type, "?Unknown?"));
+ return PAIR_ENCODE_ERROR;
+ }
+
+ /*
+ * Check if we have enough for an option header plus the
+ * enterprise-number.
+ */
+ CHECK_FREESPACE(outlen, OPT_HDR_LEN + sizeof(uint32_t));
+
+ /*
+ * Now process the vendor ID part (which is one attribute deeper)
+ */
+ da = tlv_stack[++depth];
+ FR_PROTO_STACK_PRINT(tlv_stack, depth);
+
+ if (da->type != FR_TYPE_VENDOR) {
+ fr_strerror_printf("%s: Expected type \"vsa\" got \"%s\"", __FUNCTION__,
+ fr_int2str(dict_attr_types, da->type, "?Unknown?"));
+ return PAIR_ENCODE_ERROR;
+ }
+
+ /*
+ * Copy in the 32bit PEN (Private Enterprise Number)
+ */
+ p += OPT_HDR_LEN;
+ pen = htonl(da->attr);
+ memcpy(p, &pen, sizeof(pen));
+
+ /*
+ * Encode the vendor specific option header
+ * i.e. OPTION_VENDOR_OPTS and whatever the length of the vendor
+ * specific attribute was.
+ */
+ slen = encode_vendor_attr_hdr(p, end - p, tlv_stack, depth, cursor, encoder_ctx);
+ if (slen < 0) return slen;
+ p += slen;
+
+ encode_option_hdr(out, outlen, da->attr, p - out);
+
+#ifndef NDEBUG
+ FR_PROTO_HEX_DUMP("Done VSIO header", out, end - p);
+#endif
+
+ return p - out;
}
/** Encode a DHCPv6 option and any sub-options.
*
- * @param[out] out Where to write encoded DHCP attributes.
- * @param[in] outlen Length of out buffer.
- * @param[in] cursor with current VP set to the option to be encoded. Will be advanced to the next option to encode.
- * @param[in] encoder_ctx containing parameters for the encoder.
+ * @param[out] out Where to write encoded DHCP attributes.
+ * @param[in] outlen Length of out buffer.
+ * @param[in] cursor with current VP set to the option to be encoded.
+ * Will be advanced to the next option to encode.
+ * @param[in] encoder_ctx containing parameters for the encoder.
* @return
* - > 0 length of data written.
* - < 0 error.
- * - 0 not valid option for DHCP (skipping).
*/
-ssize_t fr_dhcpv6_encode_option(uint8_t *out, size_t outlen, fr_cursor_t *cursor, UNUSED void *encoder_ctx)
+ssize_t fr_dhcpv6_encode_option(uint8_t *out, size_t outlen, fr_cursor_t *cursor, void *encoder_ctx)
{
VALUE_PAIR *vp;
unsigned int depth = 0;
fr_dict_attr_t const *tlv_stack[FR_DICT_MAX_TLV_STACK + 1];
- ssize_t len;
+ ssize_t slen;
vp = fr_cursor_current(cursor);
- if (!vp) return -1;
+ if (!vp) return PAIR_ENCODE_ERROR;
if (vp->da->flags.internal) {
fr_strerror_printf("Attribute \"%s\" is not a DHCPv6 option", vp->da->name);
fr_cursor_next(cursor);
- return 0;
+ return PAIR_ENCODE_SKIP;
}
fr_proto_tlv_stack_build(tlv_stack, vp->da);
FR_PROTO_STACK_PRINT(tlv_stack, depth);
/*
- * We only have two types of options in DHCPv6
+ * Trim output buffer size for sanity
+ */
+ if (outlen > (OPT_HDR_LEN + UINT16_MAX)) outlen = (OPT_HDR_LEN + UINT16_MAX);
+
+ /*
+ * Deal with nested options
*/
switch (tlv_stack[depth]->type) {
case FR_TYPE_TLV:
- len = encode_tlv_hdr(out, outlen, tlv_stack, depth, cursor);
+ slen = encode_tlv_hdr(out, outlen, tlv_stack, depth, cursor, encoder_ctx);
+ break;
+
+ case FR_TYPE_VSA:
+ slen = encode_vsio_hdr(out, outlen, tlv_stack, depth, cursor, encoder_ctx);
break;
default:
- len = encode_rfc_hdr(out, outlen, tlv_stack, depth, cursor);
+ slen = encode_rfc_hdr(out, outlen, tlv_stack, depth, cursor, encoder_ctx);
break;
}
- if (len < 0) return len;
+ if (slen <= 0) return slen;
- FR_PROTO_TRACE("Complete option is %zu byte(s)", len);
- FR_PROTO_HEX_DUMP(NULL, out, len);
+ FR_PROTO_TRACE("Complete option is %zu byte(s)", slen);
+ FR_PROTO_HEX_DUMP(NULL, out, slen);
- return len;
+ return slen;
}
int total_length;
int len;
VALUE_PAIR const *vp;
- vp_cursor_t cursor;
+ fr_cursor_t cursor;
fr_radius_ctx_t packet_ctx;
packet_ctx.secret = secret;
/*
* Loop over the reply attributes for the packet.
*/
- fr_pair_cursor_init(&cursor, &vps);
- while ((vp = fr_pair_cursor_current(&cursor))) {
+ fr_cursor_init(&cursor, &vps);
+ while ((vp = fr_cursor_current(&cursor))) {
size_t last_len, room;
char const *last_name = NULL;
}
memcpy(ptr, vp->vp_octets, len);
- fr_pair_cursor_next(&cursor);
+ fr_cursor_next(&cursor);
goto next;
}
#endif
- fr_pair_cursor_next(&cursor);
+ fr_cursor_next(&cursor);
continue;
}
char const *secret, UNUSED size_t secret_len, VALUE_PAIR **vps)
{
ssize_t slen;
- vp_cursor_t cursor;
+ fr_cursor_t cursor;
uint8_t const *attr, *end;
fr_radius_ctx_t packet_ctx;
packet_ctx.vector = original + 4;
packet_ctx.root = fr_dict_root(fr_dict_internal);
- fr_pair_cursor_init(&cursor, vps);
+ fr_cursor_init(&cursor, vps);
attr = packet + 20;
end = packet + packet_len;
/** Convert a "concatenated" attribute to one long VP
*
*/
-static ssize_t decode_concat(TALLOC_CTX *ctx, vp_cursor_t *cursor,
+static ssize_t decode_concat(TALLOC_CTX *ctx, fr_cursor_t *cursor,
fr_dict_attr_t const *parent, uint8_t const *data,
size_t const packet_len)
{
p += ptr[1] - 2;
ptr += ptr[1];
}
- fr_pair_cursor_append(cursor, vp);
+ fr_cursor_append(cursor, vp);
return ptr - data;
}
/** Convert TLVs to one or more VPs
*
*/
-ssize_t fr_radius_decode_tlv(TALLOC_CTX *ctx, vp_cursor_t *cursor,
+ssize_t fr_radius_decode_tlv(TALLOC_CTX *ctx, fr_cursor_t *cursor,
fr_dict_attr_t const *parent, uint8_t const *data, size_t data_len,
void *decoder_ctx)
{
uint8_t const *p = data, *end = data + data_len;
fr_dict_attr_t const *child;
VALUE_PAIR *head = NULL;
- vp_cursor_t tlv_cursor;
+ fr_cursor_t tlv_cursor;
if (data_len < 3) return -1; /* type, length, value */
/*
* Record where we were in the list when this function was called
*/
- fr_pair_cursor_init(&tlv_cursor, &head);
+ fr_cursor_init(&tlv_cursor, &head);
while (p < end) {
ssize_t tlv_len;
if (tlv_len < 0) goto error;
p += p[1];
}
- fr_pair_cursor_merge(cursor, head); /* Wind to the end of the new pairs */
+ fr_cursor_head(&tlv_cursor);
+ fr_cursor_tail(cursor);
+ fr_cursor_merge(cursor, &tlv_cursor); /* Wind to the end of the new pairs */
return data_len;
}
/** Convert a STRUCT to one or more VPs
*
*/
-static ssize_t fr_radius_decode_struct(TALLOC_CTX *ctx, vp_cursor_t *cursor,
+static ssize_t fr_radius_decode_struct(TALLOC_CTX *ctx, fr_cursor_t *cursor,
fr_dict_attr_t const *parent, uint8_t const *data, size_t data_len,
void *decoder_ctx)
{
uint8_t const *p = data, *end = data + data_len;
fr_dict_attr_t const *child;
VALUE_PAIR *head = NULL;
- vp_cursor_t child_cursor;
+ fr_cursor_t child_cursor;
if (data_len < 1) return -1; /* at least one byte of data */
/*
* Record where we were in the list when this function was called
*/
- fr_pair_cursor_init(&child_cursor, &head);
+ fr_cursor_init(&child_cursor, &head);
child_num = 1;
while (p < end) {
ssize_t child_len;
raw:
fr_pair_list_free(&head);
- fr_pair_cursor_init(&child_cursor, &head);
+ fr_cursor_init(&child_cursor, &head);
/*
* Build an unknown attr of the entire STRUCT.
p += child->flags.length;
child_num++; /* go to the next child */
}
- fr_pair_cursor_merge(cursor, head); /* Wind to the end of the new pairs */
+ fr_cursor_head(&child_cursor);
+ fr_cursor_tail(cursor);
+ fr_cursor_merge(cursor, &child_cursor); /* Wind to the end of the new pairs */
return data_len;
}
*
* "length" can be LONGER than just this sub-vsa.
*/
-static ssize_t decode_vsa_internal(TALLOC_CTX *ctx, vp_cursor_t *cursor,
+static ssize_t decode_vsa_internal(TALLOC_CTX *ctx, fr_cursor_t *cursor,
fr_dict_attr_t const *parent,
uint8_t const *data, size_t data_len,
void *decoder_ctx, fr_dict_vendor_t const *dv)
*
* But for the first fragment, we get passed a pointer to the "extended-attr"
*/
-static ssize_t decode_extended(TALLOC_CTX *ctx, vp_cursor_t *cursor,
+static ssize_t decode_extended(TALLOC_CTX *ctx, fr_cursor_t *cursor,
fr_dict_attr_t const *parent,
uint8_t const *data, size_t attr_len, size_t packet_len,
void *decoder_ctx)
*
* @note Called ONLY for Vendor-Specific
*/
-static ssize_t decode_wimax(TALLOC_CTX *ctx, vp_cursor_t *cursor,
+static ssize_t decode_wimax(TALLOC_CTX *ctx, fr_cursor_t *cursor,
fr_dict_attr_t const *parent,
uint8_t const *data, size_t attr_len, size_t packet_len, void *decoder_ctx, uint32_t vendor)
{
/** Convert a top-level VSA to one or more VPs
*
*/
-static ssize_t decode_vsa(TALLOC_CTX *ctx, vp_cursor_t *cursor, fr_dict_attr_t const *parent,
+static ssize_t decode_vsa(TALLOC_CTX *ctx, fr_cursor_t *cursor, fr_dict_attr_t const *parent,
uint8_t const *data, size_t attr_len, size_t packet_len,
void *decoder_ctx)
{
VALUE_PAIR *head = NULL;
fr_dict_vendor_t my_dv;
fr_dict_attr_t const *vendor_da;
- vp_cursor_t tlv_cursor;
+ fr_cursor_t tlv_cursor;
/*
* Container must be a VSA
packet_len -= 4;
total = 4;
- fr_pair_cursor_init(&tlv_cursor, &head);
+ fr_cursor_init(&tlv_cursor, &head);
while (attr_len > 0) {
ssize_t vsa_len;
packet_len -= vsa_len;
total += vsa_len;
}
- fr_pair_cursor_merge(cursor, head);
+ fr_cursor_head(&tlv_cursor);
+ fr_cursor_tail(cursor);
+ fr_cursor_merge(cursor, &tlv_cursor);
/*
* When the unknown attributes were created by
* - Length on success.
* - -1 on failure.
*/
-ssize_t fr_radius_decode_pair_value(TALLOC_CTX *ctx, vp_cursor_t *cursor, fr_dict_attr_t const *parent,
+ssize_t fr_radius_decode_pair_value(TALLOC_CTX *ctx, fr_cursor_t *cursor, fr_dict_attr_t const *parent,
uint8_t const *data, size_t const attr_len, size_t const packet_len,
void *decoder_ctx)
{
}
vp->type = VT_DATA;
vp->vp_tainted = true;
- fr_pair_cursor_append(cursor, vp);
+ fr_cursor_append(cursor, vp);
return attr_len;
}
/** Create a "normal" VALUE_PAIR from the given data
*
*/
-ssize_t fr_radius_decode_pair(TALLOC_CTX *ctx, vp_cursor_t *cursor,
+ssize_t fr_radius_decode_pair(TALLOC_CTX *ctx, fr_cursor_t *cursor,
uint8_t const *data, size_t data_len, void *decoder_ctx)
{
ssize_t rcode;
*/
vp = fr_pair_afrom_da(ctx, da);
if (!vp) return -1;
- fr_pair_cursor_append(cursor, vp);
+ fr_cursor_append(cursor, vp);
vp->vp_tainted = true; /* not REALLY necessary, but what the heck */
return 2;
static ssize_t encode_value(uint8_t *out, size_t outlen,
fr_dict_attr_t const **tlv_stack, int depth,
- vp_cursor_t *cursor, void *encoder_ctx);
+ fr_cursor_t *cursor, void *encoder_ctx);
static ssize_t encode_rfc_hdr_internal(uint8_t *out, size_t outlen,
fr_dict_attr_t const **tlv_stack, unsigned int depth,
- vp_cursor_t *cursor, void *encoder_ctx);
+ fr_cursor_t *cursor, void *encoder_ctx);
static ssize_t encode_tlv_hdr(uint8_t *out, size_t outlen,
fr_dict_attr_t const **tlv_stack, unsigned int depth,
- vp_cursor_t *cursor, void *encoder_ctx);
+ fr_cursor_t *cursor, void *encoder_ctx);
/** Determine if the current attribute is encodable, or find the first one that is
* @param cursor to iterate over.
* @return encodable VALUE_PAIR, or NULL if none available.
*/
-static inline VALUE_PAIR *first_encodable(vp_cursor_t *cursor)
+static inline VALUE_PAIR *first_encodable(fr_cursor_t *cursor)
{
VALUE_PAIR *vp;
- for (vp = fr_pair_cursor_current(cursor); vp && vp->da->flags.internal; vp = fr_pair_cursor_next(cursor));
- return fr_pair_cursor_current(cursor);
+ for (vp = fr_cursor_current(cursor); vp && vp->da->flags.internal; vp = fr_cursor_next(cursor));
+ return fr_cursor_current(cursor);
}
/** Find the next attribute to encode
* @param cursor to iterate over.
* @return encodable VALUE_PAIR, or NULL if none available.
*/
-static inline VALUE_PAIR *next_encodable(vp_cursor_t *cursor)
+static inline VALUE_PAIR *next_encodable(fr_cursor_t *cursor)
{
VALUE_PAIR *vp;
for (;;) {
- vp = fr_pair_cursor_next(cursor);
+ vp = fr_cursor_next(cursor);
if (!vp || !vp->da->flags.internal) break;
}
- return fr_pair_cursor_current(cursor);
+ return fr_cursor_current(cursor);
}
/** Encode a CHAP password
static ssize_t encode_struct(uint8_t *out, size_t outlen,
fr_dict_attr_t const **tlv_stack, unsigned int depth,
- vp_cursor_t *cursor, void *encoder_ctx)
+ fr_cursor_t *cursor, void *encoder_ctx)
{
ssize_t len;
unsigned int child_num = 1;
uint8_t *p = out;
- VALUE_PAIR const *vp = fr_pair_cursor_current(cursor);
+ VALUE_PAIR const *vp = fr_cursor_current(cursor);
fr_dict_attr_t const *da = tlv_stack[depth];
- VP_VERIFY(fr_pair_cursor_current(cursor));
+ VP_VERIFY(fr_cursor_current(cursor));
FR_PROTO_STACK_PRINT(tlv_stack, depth);
if (tlv_stack[depth]->type != FR_TYPE_STRUCT) {
/*
* If nothing updated the attribute, stop
*/
- if (!fr_pair_cursor_current(cursor) || (vp == fr_pair_cursor_current(cursor))) break;
+ if (!fr_cursor_current(cursor) || (vp == fr_cursor_current(cursor))) break;
/*
* We can encode multiple sub TLVs, if after
* at this depth is the same.
*/
if (da != tlv_stack[depth]) break;
- vp = fr_pair_cursor_current(cursor);
+ vp = fr_cursor_current(cursor);
FR_PROTO_HEX_DUMP("Done STRUCT", out, p - out);
}
static ssize_t encode_tlv_hdr_internal(uint8_t *out, size_t outlen,
fr_dict_attr_t const **tlv_stack, unsigned int depth,
- vp_cursor_t *cursor, void *encoder_ctx)
+ fr_cursor_t *cursor, void *encoder_ctx)
{
ssize_t len;
uint8_t *p = out;
- VALUE_PAIR const *vp = fr_pair_cursor_current(cursor);
+ VALUE_PAIR const *vp = fr_cursor_current(cursor);
fr_dict_attr_t const *da = tlv_stack[depth];
while (outlen >= 5) {
/*
* If nothing updated the attribute, stop
*/
- if (!fr_pair_cursor_current(cursor) || (vp == fr_pair_cursor_current(cursor))) break;
+ if (!fr_cursor_current(cursor) || (vp == fr_cursor_current(cursor))) break;
/*
* We can encode multiple sub TLVs, if after
* at this depth is the same.
*/
if (da != tlv_stack[depth]) break;
- vp = fr_pair_cursor_current(cursor);
+ vp = fr_cursor_current(cursor);
FR_PROTO_HEX_DUMP("Done TLV", out, p - out);
}
static ssize_t encode_tlv_hdr(uint8_t *out, size_t outlen,
fr_dict_attr_t const **tlv_stack, unsigned int depth,
- vp_cursor_t *cursor, void *encoder_ctx)
+ fr_cursor_t *cursor, void *encoder_ctx)
{
ssize_t len;
- VP_VERIFY(fr_pair_cursor_current(cursor));
+ VP_VERIFY(fr_cursor_current(cursor));
FR_PROTO_STACK_PRINT(tlv_stack, depth);
if (tlv_stack[depth]->type != FR_TYPE_TLV) {
*/
static ssize_t encode_value(uint8_t *out, size_t outlen,
fr_dict_attr_t const **tlv_stack, int depth,
- vp_cursor_t *cursor, void *encoder_ctx)
+ fr_cursor_t *cursor, void *encoder_ctx)
{
size_t offset;
ssize_t len;
uint8_t const *data = NULL;
uint8_t *ptr = out;
uint8_t buffer[64];
- VALUE_PAIR const *vp = fr_pair_cursor_current(cursor);
+ VALUE_PAIR const *vp = fr_cursor_current(cursor);
fr_dict_attr_t const *da = tlv_stack[depth];
fr_radius_ctx_t *packet_ctx = encoder_ctx;
len = fr_radius_attr_len(vp);
switch (da->type) {
+ /*
+ * If asked to encode more data than allowed, we
+ * encode only the allowed data.
+ */
case FR_TYPE_OCTETS:
- /*
- * If asked to encode more data than allowed, we
- * encode only the allowed data.
- */
- if (da->flags.length && (len > da->flags.length)) {
- len = da->flags.length;
- }
- /* FALL-THROUGH */
-
case FR_TYPE_STRING:
+ if (da->flags.length && (len > da->flags.length)) len = da->flags.length;
data = vp->vp_ptr;
break;
case FR_TYPE_IPV6_PREFIX:
buffer[0] = 0;
buffer[1] = vp->vp_ip.prefix;
- len = vp->vp_ip.prefix >> 3; /* Convert bits to whole bytes */
- memcpy(buffer, vp->vp_ipv6addr, len); /* Only copy the minimum number of address bytes required */
- len += 2; /* Reserved and prefix bytes */
+ len = vp->vp_ip.prefix >> 3; /* Convert bits to whole bytes */
+ memcpy(buffer + 2, vp->vp_ipv6addr, len); /* Only copy the minimum number of address bytes required */
+ len += 2; /* Reserved and prefix bytes */
data = buffer;
break;
case FR_TYPE_IPV4_PREFIX:
buffer[0] = 0;
buffer[1] = vp->vp_ip.prefix;
- memcpy(buffer, &vp->vp_ipv4addr, sizeof(vp->vp_ipv4addr));
+ memcpy(buffer + 2, &vp->vp_ipv4addr, sizeof(vp->vp_ipv4addr));
data = buffer;
break;
}
/*
- * Bound the data to the calling size
+ * Bind the data to the calling size
*/
if (len > (ssize_t)outlen) len = outlen;
*/
static int encode_extended_hdr(uint8_t *out, size_t outlen,
fr_dict_attr_t const **tlv_stack, unsigned int depth,
- vp_cursor_t *cursor, void *encoder_ctx)
+ fr_cursor_t *cursor, void *encoder_ctx)
{
int len;
fr_type_t attr_type;
fr_type_t vsa_type;
#endif
uint8_t *start = out;
- VALUE_PAIR const *vp = fr_pair_cursor_current(cursor);
+ VALUE_PAIR const *vp = fr_cursor_current(cursor);
VP_VERIFY(vp);
FR_PROTO_STACK_PRINT(tlv_stack, depth);
*/
static ssize_t encode_concat(uint8_t *out, size_t outlen,
fr_dict_attr_t const **tlv_stack, unsigned int depth,
- vp_cursor_t *cursor, UNUSED void *encoder_ctx)
+ fr_cursor_t *cursor, UNUSED void *encoder_ctx)
{
uint8_t *ptr = out;
uint8_t const *p;
size_t len, left;
- VALUE_PAIR const *vp = fr_pair_cursor_current(cursor);
+ VALUE_PAIR const *vp = fr_cursor_current(cursor);
FR_PROTO_STACK_PRINT(tlv_stack, depth);
*/
static ssize_t encode_rfc_hdr_internal(uint8_t *out, size_t outlen,
fr_dict_attr_t const **tlv_stack, unsigned int depth,
- vp_cursor_t *cursor, void *encoder_ctx)
+ fr_cursor_t *cursor, void *encoder_ctx)
{
ssize_t len;
*/
static ssize_t encode_vendor_attr_hdr(uint8_t *out, size_t outlen,
fr_dict_attr_t const **tlv_stack, unsigned int depth,
- vp_cursor_t *cursor, void *encoder_ctx)
+ fr_cursor_t *cursor, void *encoder_ctx)
{
ssize_t len;
size_t hdr_len;
*/
static int encode_wimax_hdr(uint8_t *out, size_t outlen,
fr_dict_attr_t const **tlv_stack, unsigned int depth,
- vp_cursor_t *cursor, void *encoder_ctx)
+ fr_cursor_t *cursor, void *encoder_ctx)
{
int len;
uint32_t lvalue;
uint8_t *start = out;
- VALUE_PAIR const *vp = fr_pair_cursor_current(cursor);
+ VALUE_PAIR const *vp = fr_cursor_current(cursor);
VP_VERIFY(vp);
FR_PROTO_STACK_PRINT(tlv_stack, depth);
*/
static int encode_vsa_hdr(uint8_t *out, size_t outlen,
fr_dict_attr_t const **tlv_stack, unsigned int depth,
- vp_cursor_t *cursor, void *encoder_ctx)
+ fr_cursor_t *cursor, void *encoder_ctx)
{
ssize_t len;
uint32_t lvalue;
/*
* Double-check for WiMAX format.
*/
- if (da->vendor == VENDORPEC_WIMAX) return encode_wimax_hdr(out, outlen, tlv_stack, depth + 1, cursor, encoder_ctx);
+ if (da->vendor == VENDORPEC_WIMAX) {
+ return encode_wimax_hdr(out, outlen, tlv_stack, depth + 1, cursor, encoder_ctx);
+ }
/*
* Not enough freespace for: attr, len, vendor-id
*
*/
static int encode_rfc_hdr(uint8_t *out, size_t outlen, fr_dict_attr_t const **tlv_stack, unsigned int depth,
- vp_cursor_t *cursor, void *encoder_ctx)
+ fr_cursor_t *cursor, void *encoder_ctx)
{
- VALUE_PAIR const *vp = fr_pair_cursor_current(cursor);
+ VALUE_PAIR const *vp = fr_cursor_current(cursor);
/*
* Sanity checks
* - 0 Nothing to encode (or attribute skipped).
* - <0 an error occurred.
*/
-ssize_t fr_radius_encode_pair(uint8_t *out, size_t outlen, vp_cursor_t *cursor, void *encoder_ctx)
+ssize_t fr_radius_encode_pair(uint8_t *out, size_t outlen, fr_cursor_t *cursor, void *encoder_ctx)
{
VALUE_PAIR const *vp;
int ret;
/*
* We couldn't do it, so we didn't do anything.
*/
- if (fr_pair_cursor_current(cursor) == vp) {
+ if (fr_cursor_current(cursor) == vp) {
fr_strerror_printf("%s: Nested attribute structure too large to encode", __FUNCTION__);
return -1;
}
uint8_t *ptr;
radius_packet_t *hdr;
VALUE_PAIR *head = NULL;
- vp_cursor_t cursor, out;
+ fr_cursor_t cursor, out;
fr_radius_ctx_t packet_ctx;
packet_ctx.secret = secret;
packet_length = packet->data_len - RADIUS_HDR_LEN;
num_attributes = 0;
- fr_pair_cursor_init(&cursor, &head);
+ fr_cursor_init(&cursor, &head);
/*
* Loop over the attributes, decoding them into VPs.
/*
* Count the ones which were just added
*/
- while (fr_pair_cursor_next(&cursor)) num_attributes++;
+ while (fr_cursor_next(&cursor)) num_attributes++;
/*
* VSA's may not have been counted properly in
packet_length -= my_len;
}
- fr_pair_cursor_init(&out, &packet->vps);
- fr_pair_cursor_last(&out); /* Move insertion point to the end of the list */
- fr_pair_cursor_merge(&out, head);
+ fr_cursor_init(&out, &packet->vps);
+ fr_cursor_tail(&out); /* Move insertion point to the end of the list */
+ fr_cursor_head(&cursor);
+ fr_cursor_merge(&out, &cursor);
/*
* Merge information from the outside world into our
int fr_radius_encode_chap_password(uint8_t *output, RADIUS_PACKET *packet, int id, VALUE_PAIR *password);
-ssize_t fr_radius_encode_pair(uint8_t *out, size_t outlen, vp_cursor_t *cursor, void *encoder_ctx);
+ssize_t fr_radius_encode_pair(uint8_t *out, size_t outlen, fr_cursor_t *cursor, void *encoder_ctx);
/*
* protocols/radius/decode.c
ssize_t fr_radius_decode_tunnel_password(uint8_t *encpw, size_t *len, char const *secret,
uint8_t const *vector, bool tunnel_password_zeros);
-ssize_t fr_radius_decode_pair_value(TALLOC_CTX *ctx, vp_cursor_t *cursor, fr_dict_attr_t const *parent,
+ssize_t fr_radius_decode_pair_value(TALLOC_CTX *ctx, fr_cursor_t *cursor, fr_dict_attr_t const *parent,
uint8_t const *data, size_t const attr_len, size_t const packet_len,
void *decoder_ctx);
-ssize_t fr_radius_decode_tlv(TALLOC_CTX *ctx, vp_cursor_t *cursor, fr_dict_attr_t const *parent,
+ssize_t fr_radius_decode_tlv(TALLOC_CTX *ctx, fr_cursor_t *cursor, fr_dict_attr_t const *parent,
uint8_t const *data, size_t data_len,
void *decoder_ctx);
-ssize_t fr_radius_decode_pair(TALLOC_CTX *ctx, vp_cursor_t *cursor, uint8_t const *data, size_t data_len,
+ssize_t fr_radius_decode_pair(TALLOC_CTX *ctx, fr_cursor_t *cursor, uint8_t const *data, size_t data_len,
void *decoder_ctx);
#endif /* _FR_RADIUS_RADIUS_H */