From: Alan T. DeKok Date: Sun, 4 Oct 2020 14:54:38 +0000 (-0400) Subject: start of encoding Relay-Message X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=a89a99933cdb68beafa37f3de3eae9dc3df5e086;p=thirdparty%2Ffreeradius-server.git start of encoding Relay-Message The tests for encoding relay packets don't yet work, for a few reasons. * fr_value_box_from_str() is passed dst_enumv, and it then checks if the value is a valid _attribute_ name? Which doesn't work for IPv6 addresses, as they contain ":" * the fr_pair_list_afrom_substr() function doesn't treat "Attr-" as special, so it tries to look it up in the various dictionaries, and fails. * the fr_dhcpv6_encode() function is failing for nested packets, and doesn't return a useful error message --- diff --git a/src/modules/proto_dhcpv6/proto_dhcpv6.c b/src/modules/proto_dhcpv6/proto_dhcpv6.c index 6b6324121a2..3cadb95a745 100644 --- a/src/modules/proto_dhcpv6/proto_dhcpv6.c +++ b/src/modules/proto_dhcpv6/proto_dhcpv6.c @@ -390,8 +390,8 @@ static ssize_t mod_encode(void const *instance, REQUEST *request, uint8_t *buffe if (data_len > 0) return data_len; } - data_len = fr_dhcpv6_encode(buffer, buffer_len, (uint8_t const *) original, request->reply->code, - request->reply->vps); + data_len = fr_dhcpv6_encode(buffer, buffer_len, request->packet->data, request->packet->data_len, + request->reply->code, request->reply->vps); if (data_len < 0) { RPEDEBUG("Failed encoding DHCPv6 reply"); return -1; diff --git a/src/protocols/dhcpv6/base.c b/src/protocols/dhcpv6/base.c index e46c640722e..9012ea0a750 100644 --- a/src/protocols/dhcpv6/base.c +++ b/src/protocols/dhcpv6/base.c @@ -739,7 +739,7 @@ void *fr_dhcpv6_next_encodable(void **prev, void *to_eval, void *uctx) /** Encode a DHCPv6 packet * */ -ssize_t fr_dhcpv6_encode(uint8_t *packet, size_t packet_len, uint8_t const *original, +ssize_t fr_dhcpv6_encode(uint8_t *packet, size_t packet_len, uint8_t const *original, size_t length, int msg_type, VALUE_PAIR *vps) { VALUE_PAIR *vp; @@ -816,6 +816,9 @@ ssize_t fr_dhcpv6_encode(uint8_t *packet, size_t packet_len, uint8_t const *orig encode_options: packet_ctx.root = root; + packet_ctx.original = original; + packet_ctx.original_length = length; + end = packet + packet_len; fr_cursor_talloc_iter_init(&cursor, &vps, fr_dhcpv6_next_encodable, dict_dhcpv6, VALUE_PAIR); diff --git a/src/protocols/dhcpv6/dhcpv6.h b/src/protocols/dhcpv6/dhcpv6.h index aeb2b15d99e..93de2f6d7f9 100644 --- a/src/protocols/dhcpv6/dhcpv6.h +++ b/src/protocols/dhcpv6/dhcpv6.h @@ -112,6 +112,8 @@ typedef struct CC_HINT(__packed__) { typedef struct { fr_dict_attr_t const *root; //!< Root attribute of the dictionary. + uint8_t const *original; //!< original packet + size_t original_length; //!< length of the original packet } fr_dhcpv6_encode_ctx_t; typedef struct { @@ -134,7 +136,7 @@ bool fr_dhcpv6_ok(uint8_t const *packet, size_t packet_len, bool fr_dhcpv6_verify(uint8_t const *packet, size_t packet_len, fr_dhcpv6_decode_ctx_t const *packet_ctx, bool from_server); -ssize_t fr_dhcpv6_encode(uint8_t *packet, size_t packet_len, uint8_t const *original, +ssize_t fr_dhcpv6_encode(uint8_t *packet, size_t packet_len, uint8_t const *original, size_t length, int msg_type, VALUE_PAIR *vps); ssize_t fr_dhcpv6_decode(TALLOC_CTX *ctx, uint8_t const *packet, size_t packet_len, diff --git a/src/protocols/dhcpv6/encode.c b/src/protocols/dhcpv6/encode.c index 9134f82bb29..303b6453e12 100644 --- a/src/protocols/dhcpv6/encode.c +++ b/src/protocols/dhcpv6/encode.c @@ -727,7 +727,7 @@ static ssize_t encode_vsio_hdr(fr_dbuff_t *dbuff, } /* - * Copy in the 32bit PEN (Private Enterprise Number) +(&work_dbuff, &da_stack, depth, cursor, encoder_ctx); * Copy in the 32bit PEN (Private Enterprise Number) */ fr_dbuff_advance(&work_dbuff, OPT_HDR_LEN); fr_dbuff_in(&work_dbuff, dv->attr); @@ -774,6 +774,77 @@ static ssize_t encode_vsio_hdr(fr_dbuff_t *dbuff, return fr_dbuff_set(dbuff, &work_dbuff); } +/** Encode a Relay-Message + * + * Header + stuff + */ +static ssize_t encode_relay_message(fr_dbuff_t *dbuff, + fr_da_stack_t *da_stack, unsigned int depth, + fr_cursor_t *cursor, void *encoder_ctx) +{ + fr_dbuff_t work_dbuff = FR_DBUFF_NO_ADVANCE(dbuff); + fr_dbuff_t hdr_dbuff = FR_DBUFF_NO_ADVANCE(dbuff); + fr_dict_attr_t const *da = da_stack->da[depth]; + ssize_t len; + uint8_t const *original = NULL; + size_t original_length = 0; + VALUE_PAIR *vp; + fr_dhcpv6_encode_ctx_t *packet_ctx = encoder_ctx; + + FR_PROTO_STACK_PRINT(da_stack, depth); + + vp = fr_cursor_current(cursor); + if (!vp->vp_group) { + vp = fr_cursor_next(cursor); + fr_proto_da_stack_build(da_stack, vp ? vp->da : NULL); + return PAIR_ENCODE_SKIPPED; + } + + /* + * Make space for the header... + */ + FR_DBUFF_ADVANCE_RETURN(&work_dbuff, OPT_HDR_LEN); + + /* + * Pass the original packet to the packet encode routine. + */ + if (packet_ctx->original) { + uint8_t const *options; + + if (packet_ctx->original[0] == FR_DHCPV6_RELAY_FORWARD) { + options = packet_ctx->original + 2 + 32; + } else { + options = packet_ctx->original + 4; + } + + original = fr_dhcpv6_option_find(options, packet_ctx->original + packet_ctx->original_length, + attr_relay_message->attr); + if (original) { + original_length = (original[2] << 8) | original[3]; + original += 4; + } + } + + vp = fr_cursor_current(cursor); + + len = fr_dhcpv6_encode(work_dbuff.p, fr_dbuff_remaining(&work_dbuff), original, original_length, FR_DHCPV6_RELAY_REPLY, vp->vp_group); + if (len <= 0) return -1; + + /* + * Write out the option number and length (before the value we just wrote) + */ + encode_option_hdr(&hdr_dbuff, (uint16_t)da->attr, (uint16_t) (fr_dbuff_used(&work_dbuff) - OPT_HDR_LEN)); + +#ifndef NDEBUG + FR_PROTO_HEX_DUMP(dbuff->p, fr_dbuff_used(&work_dbuff), "Done RFC header"); +#endif + + vp = fr_cursor_next(cursor); + fr_proto_da_stack_build(da_stack, vp ? vp->da : NULL); + + return fr_dbuff_set(dbuff, &work_dbuff); +} + /** Encode a DHCPv6 option and any sub-options. * * @param[out] out Where to write encoded DHCP attributes. @@ -830,6 +901,13 @@ static ssize_t encode_option(fr_dbuff_t *dbuff, fr_cursor_t *cursor, void * enco len = encode_vsio_hdr(&work_dbuff, &da_stack, depth, cursor, encoder_ctx); break; + case FR_TYPE_GROUP: + if (da_stack.da[depth] == attr_relay_message) { + len = encode_relay_message(&work_dbuff, &da_stack, depth, cursor, encoder_ctx); + break; + } + FALL_THROUGH; + default: len = encode_rfc_hdr(&work_dbuff, &da_stack, depth, cursor, encoder_ctx); break; @@ -870,7 +948,7 @@ static ssize_t fr_dhcpv6_encode_proto(UNUSED TALLOC_CTX *ctx, VALUE_PAIR *vps, u { // fr_dhcpv6_decode_ctx_t *test_ctx = talloc_get_type_abort(proto_ctx, fr_dhcpv6_decode_ctx_t); - return fr_dhcpv6_encode(data, data_len, NULL, 0, vps); + return fr_dhcpv6_encode(data, data_len, NULL, 0, 0, vps); } /*