]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
move "eval pair" code from unlang to tmpl code
authorAlan T. DeKok <aland@freeradius.org>
Tue, 28 Jun 2022 21:09:01 +0000 (17:09 -0400)
committerAlan T. DeKok <aland@freeradius.org>
Thu, 30 Jun 2022 11:24:31 +0000 (07:24 -0400)
This function is only for attributes which can be done
synchronously, i.e. without blocking.

src/lib/server/base.c
src/lib/server/tmpl.h
src/lib/server/tmpl_eval.c
src/lib/unlang/xlat_eval.c

index 8ad85d9a6fec8e667e17cd6753f8dd3a04be5970..7fda9a401ecbe9fe21978e5024efb44b04b37814 100644 (file)
@@ -39,6 +39,11 @@ RCSID("$Id$")
  */
 int server_init(CONF_SECTION *cs)
 {
+       /*
+        *      Initialize the dictionary attributes needed by the tmpl code.
+        */
+       if (tmpl_global_init() < 0) return -1;
+
        /*
         *      Load dictionary attributes used
         *      for requests.
@@ -135,4 +140,9 @@ void server_free(void)
         *      Free the internal dictionaries the request uses
         */
        request_global_free();
+
+       /*
+        *      Free the internal dictionaries the tmpl code uses
+        */
+       tmpl_global_free();
 }
index ff0e33d9a914f463b9e4f009c6c18a7b39d7c712..104e0c482f7c081873fd50d3915af77c128fb640 100644 (file)
@@ -1133,6 +1133,8 @@ int                       tmpl_extents_build_to_leaf_parent(fr_dlist_head_t *leaf, fr_dlist_head_t *
                                                   tmpl_t const *vpt) CC_HINT(nonnull);
 
 void                   tmpl_extents_debug(fr_dlist_head_t *head) CC_HINT(nonnull);
+
+int                    tmpl_eval_pair(TALLOC_CTX *ctx, fr_value_box_list_t *out, request_t *request, tmpl_t const *vpt);
 /** @} */
 
 ssize_t                        tmpl_preparse(char const **out, size_t *outlen, char const *in, size_t inlen,
@@ -1146,6 +1148,9 @@ fr_pair_t         *tmpl_get_list(request_t *request, tmpl_t const *vpt) CC_HINT(nonnull
 
 int                    tmpl_value_list_insert_tail(fr_value_box_list_t *list, fr_value_box_t *vb, tmpl_t const *vpt) CC_HINT(nonnull);
 
+int                    tmpl_global_init(void);
+void                   tmpl_global_free(void);
+
 #undef _CONST
 
 #ifdef __cplusplus
index 57e961e2b3d19eba2b244a56ac23ee6eff6eebcb..3026a8e092ddb96f54d522ebad24bed51a882bef 100644 (file)
@@ -32,11 +32,55 @@ RCSID("$Id$")
 #include <freeradius-devel/server/exec_legacy.h>
 #include <freeradius-devel/server/tmpl.h>
 #include <freeradius-devel/server/tmpl_dcursor.h>
+#include <freeradius-devel/server/client.h>
+#include <freeradius-devel/unlang/call.h>
 #include <freeradius-devel/util/dlist.h>
 #include <freeradius-devel/util/proto.h>
 #include <freeradius-devel/util/value.h>
 #include <freeradius-devel/util/edit.h>
 
+static fr_dict_t const *dict_freeradius;
+static fr_dict_t const *dict_radius;
+
+extern fr_dict_autoload_t tmpl_dict[];
+fr_dict_autoload_t tmpl_dict[] = {
+       { .out = &dict_freeradius, .proto = "freeradius" },
+       { .out = &dict_radius, .proto = "radius" }, /* @todo - remove RADIUS from the server core... */
+       { NULL }
+};
+
+static fr_dict_attr_t const *attr_client_ip_address;
+static fr_dict_attr_t const *attr_client_shortname;
+static fr_dict_attr_t const *attr_packet_dst_ip_address;
+static fr_dict_attr_t const *attr_packet_dst_ipv6_address;
+static fr_dict_attr_t const *attr_packet_dst_port;
+static fr_dict_attr_t const *attr_packet_src_ip_address;
+static fr_dict_attr_t const *attr_packet_src_ipv6_address;
+static fr_dict_attr_t const *attr_packet_src_port;
+static fr_dict_attr_t const *attr_packet_type;
+static fr_dict_attr_t const *attr_packet_authentication_vector;
+static fr_dict_attr_t const *attr_request_processing_stage;
+static fr_dict_attr_t const *attr_virtual_server;
+static fr_dict_attr_t const *attr_module_return_code;
+
+static fr_dict_attr_autoload_t tmpl_dict_attr[] = {
+       { .out = &attr_client_ip_address, .name = "Client-IP-Address", .type = FR_TYPE_IPV4_ADDR, .dict = &dict_freeradius },
+       { .out = &attr_client_shortname, .name = "Client-Shortname", .type = FR_TYPE_STRING, .dict = &dict_freeradius },
+       { .out = &attr_module_return_code, .name = "Module-Return-Code", .type = FR_TYPE_UINT32, .dict = &dict_freeradius },
+       { .out = &attr_packet_dst_ip_address, .name = "Packet-Dst-IP-Address", .type = FR_TYPE_IPV4_ADDR, .dict = &dict_freeradius },
+       { .out = &attr_packet_dst_ipv6_address, .name = "Packet-Dst-IPV6-Address", .type = FR_TYPE_IPV6_ADDR, .dict = &dict_freeradius },
+       { .out = &attr_packet_dst_port, .name = "Packet-Dst-Port", .type = FR_TYPE_UINT16, .dict = &dict_freeradius },
+       { .out = &attr_packet_src_ip_address, .name = "Packet-Src-IP-Address", .type = FR_TYPE_IPV4_ADDR, .dict = &dict_freeradius },
+       { .out = &attr_packet_src_ipv6_address, .name = "Packet-Src-IPv6-Address", .type = FR_TYPE_IPV6_ADDR, .dict = &dict_freeradius },
+       { .out = &attr_packet_src_port, .name = "Packet-Src-Port", .type = FR_TYPE_UINT16, .dict = &dict_freeradius },
+       { .out = &attr_request_processing_stage, .name = "Request-Processing-Stage", .type = FR_TYPE_STRING, .dict = &dict_freeradius },
+       { .out = &attr_virtual_server, .name = "Virtual-Server", .type = FR_TYPE_STRING, .dict = &dict_freeradius },
+
+       { .out = &attr_packet_authentication_vector, .name = "Packet-Authentication-Vector", .type = FR_TYPE_OCTETS, .dict = &dict_radius },
+       { .out = &attr_packet_type, .name = "Packet-Type", .type = FR_TYPE_UINT32, .dict = &dict_radius },
+       { NULL }
+};
+
 /** Resolve a #tmpl_t into an #fr_pair_t
  *
  * @param[in] request containing the target lists.
@@ -1027,3 +1071,295 @@ int tmpl_value_list_insert_tail(fr_value_box_list_t *list, fr_value_box_t *box,
        fr_dlist_insert_tail(list, box);
        return 0;
 }
+
+/** Gets the value of a virtual attribute
+ *
+ * These attribute *may* be overloaded by the user using real attribute.
+ *
+ * @todo There should be a virtual attribute registry.
+ *
+ * @param[in] ctx      to allocate boxed value, and buffers in.
+ * @param[out] out     Where to write the boxed value.
+ * @param[in] request  The current request.
+ * @param[in] vpt      Representing the attribute.
+ * @return
+ *     - <0    on memory allocation errors.
+ *     - 0     success.
+ */
+static int tmpl_eval_pair_virtual(TALLOC_CTX *ctx, fr_value_box_list_t *out,
+                                 request_t *request, tmpl_t const *vpt)
+{
+       fr_radius_packet_t      *packet = NULL;
+       fr_value_box_t  *value;
+
+       /*
+        *      Virtual attributes always have a count of 1
+        */
+       if (tmpl_num(vpt) == NUM_COUNT) {
+               MEM(value = fr_value_box_alloc(ctx, FR_TYPE_UINT32, NULL, false));
+               value->datum.uint32 = 1;
+               goto done;
+       }
+
+       /*
+        *      Some non-packet expansions
+        */
+       if (tmpl_da(vpt) == attr_client_shortname) {
+               RADCLIENT *client = client_from_request(request);
+               if (!client || !client->shortname) return 0;
+
+               MEM(value = fr_value_box_alloc_null(ctx));
+               if (fr_value_box_bstrdup_buffer(ctx, value, tmpl_da(vpt), client->shortname, false) < 0) {
+               error:
+                       talloc_free(value);
+                       return -1;
+               }
+               goto done;
+       }
+
+       if (tmpl_da(vpt) == attr_request_processing_stage) {
+               if (!request->component) return 0;
+
+               MEM(value = fr_value_box_alloc_null(ctx));
+               if (fr_value_box_strdup(ctx, value, tmpl_da(vpt), request->component, false) < 0) goto error;
+               goto done;
+       }
+
+       if (tmpl_da(vpt) == attr_virtual_server) {
+               if (!unlang_call_current(request)) return 0;
+
+               MEM(value = fr_value_box_alloc_null(ctx));
+               if (fr_value_box_bstrdup_buffer(ctx, value, tmpl_da(vpt),
+                                              cf_section_name2(unlang_call_current(request)), false) < 0) goto error;
+               goto done;
+       }
+
+       if (tmpl_da(vpt) == attr_module_return_code) {
+               MEM(value = fr_value_box_alloc(ctx, tmpl_da(vpt)->type, tmpl_da(vpt), false));
+               value->datum.int32 = request->rcode;
+               goto done;
+       }
+
+       /*
+        *      All of the attributes must now refer to a packet.
+        *      If there's no packet, we can't print any attribute
+        *      referencing it.
+        */
+       packet = tmpl_packet_ptr(request, tmpl_list(vpt));
+       if (!packet) return 0;
+
+       if (tmpl_da(vpt) == attr_packet_type) {
+               if (!packet || !packet->code) return 0;
+
+               MEM(value = fr_value_box_alloc(ctx, tmpl_da(vpt)->type, NULL, false));
+               value->enumv = tmpl_da(vpt);
+               value->datum.int32 = packet->code;
+
+       /*
+        *      Virtual attributes which require a temporary fr_pair_t
+        *      to be allocated. We can't use stack allocated memory
+        *      because of the talloc checks sprinkled throughout the
+        *      various VP functions.
+        */
+       } else if (tmpl_da(vpt) == attr_packet_authentication_vector) {
+               MEM(value = fr_value_box_alloc_null(ctx));
+               fr_value_box_memdup(ctx, value, tmpl_da(vpt), packet->vector, sizeof(packet->vector), true);
+
+       } else if (tmpl_da(vpt) == attr_client_ip_address) {
+               RADCLIENT *client = client_from_request(request);
+               if (client) {
+                       MEM(value = fr_value_box_alloc_null(ctx));
+                       fr_value_box_ipaddr(value, NULL, &client->ipaddr, false);       /* Enum might not match type */
+                       goto done;
+               }
+               goto src_ip_address;
+
+       } else if (tmpl_da(vpt) == attr_packet_src_ip_address) {
+       src_ip_address:
+               if (!fr_socket_is_inet(packet->socket.proto) ||
+                   (packet->socket.inet.src_ipaddr.af != AF_INET)) return 0;
+
+               MEM(value = fr_value_box_alloc_null(ctx));
+               fr_value_box_ipaddr(value, tmpl_da(vpt), &packet->socket.inet.src_ipaddr, true);
+
+       } else if (tmpl_da(vpt) == attr_packet_dst_ip_address) {
+               if (!fr_socket_is_inet(packet->socket.proto) ||
+                   (packet->socket.inet.dst_ipaddr.af != AF_INET)) return 0;
+
+               MEM(value = fr_value_box_alloc_null(ctx));
+               fr_value_box_ipaddr(value, tmpl_da(vpt), &packet->socket.inet.dst_ipaddr, true);
+
+       } else if (tmpl_da(vpt) == attr_packet_src_ipv6_address) {
+               if (!fr_socket_is_inet(packet->socket.proto) ||
+                   (packet->socket.inet.src_ipaddr.af != AF_INET6)) return 0;
+
+               MEM(value = fr_value_box_alloc_null(ctx));
+               fr_value_box_ipaddr(value, tmpl_da(vpt), &packet->socket.inet.src_ipaddr, true);
+
+       } else if (tmpl_da(vpt) == attr_packet_dst_ipv6_address) {
+               if (!fr_socket_is_inet(packet->socket.proto) ||
+                   (packet->socket.inet.dst_ipaddr.af != AF_INET6)) return 0;
+
+               MEM(value = fr_value_box_alloc_null(ctx));
+               fr_value_box_ipaddr(value, tmpl_da(vpt), &packet->socket.inet.dst_ipaddr, true);
+
+       } else if (tmpl_da(vpt) == attr_packet_src_port) {
+               if (!fr_socket_is_inet(packet->socket.proto)) return 0;
+
+               MEM(value = fr_value_box_alloc(ctx, tmpl_da(vpt)->type, NULL, true));
+               value->datum.uint16 = packet->socket.inet.src_port;
+
+       } else if (tmpl_da(vpt) == attr_packet_dst_port) {
+               if (!fr_socket_is_inet(packet->socket.proto)) return 0;
+
+               MEM(value = fr_value_box_alloc(ctx, tmpl_da(vpt)->type, NULL, true));
+               value->datum.uint16 = packet->socket.inet.dst_port;
+
+       } else {
+               RERROR("Attribute \"%s\" incorrectly marked as virtual", tmpl_da(vpt)->name);
+               return -1;
+       }
+
+done:
+       fr_dlist_insert_tail(out, value);
+
+       return 0;
+}
+
+
+/** Gets the value of a real or virtual attribute
+ *
+ * @param[in] ctx      to allocate boxed value, and buffers in.
+ * @param[out] out     Where to write the boxed value.
+ * @param[in] request  The current request.
+ * @param[in] vpt      Representing the attribute.
+ * @return
+ *     - <0            we failed getting a value for the attribute.
+ *     - 0             we successfully evaluated the tmpl
+ */
+int tmpl_eval_pair(TALLOC_CTX *ctx, fr_value_box_list_t *out, request_t *request, tmpl_t const *vpt)
+{
+       fr_pair_t               *vp = NULL;
+       fr_value_box_t          *value;
+
+       fr_dcursor_t            cursor;
+       tmpl_dcursor_ctx_t      cc;
+
+       int                     ret = 0;
+
+       fr_assert(tmpl_is_attr(vpt) || tmpl_is_list(vpt));
+
+       /*
+        *      See if we're dealing with an attribute in the request
+        *
+        *      This allows users to manipulate virtual attributes as if
+        *      they were real ones.
+        */
+       vp = tmpl_dcursor_init(NULL, NULL, &cc, &cursor, request, vpt);
+
+       /*
+        *      We didn't find the VP in a list, check to see if it's
+        *      virtual.  This allows the caller to "realize" the
+        *      attribute, and we then prefer the realized version to
+        *      the virtual one.
+        */
+       if (!vp) {
+               if (tmpl_is_attr(vpt) && tmpl_da(vpt)->flags.virtual) {
+                       ret = tmpl_eval_pair_virtual(ctx, out, request, vpt);
+                       goto done;
+               }
+
+               /*
+                *      Zero count.
+                */
+               if (tmpl_num(vpt) == NUM_COUNT) {
+                       value = fr_value_box_alloc(ctx, FR_TYPE_UINT32, NULL, false);
+                       if (!value) {
+                       oom:
+                               fr_strerror_const("Out of memory");
+                               ret = XLAT_ACTION_FAIL;
+                               goto done;
+                       }
+                       value->datum.int32 = 0;
+                       fr_dlist_insert_tail(out, value);
+               } /* Fall through to being done */
+
+               goto done;
+       }
+
+       switch (tmpl_num(vpt)) {
+       /*
+        *      Return a count of the VPs.
+        */
+       case NUM_COUNT:
+       {
+               uint32_t                count = 0;
+
+               for (vp = fr_dcursor_current(&cursor);
+                    vp;
+                    vp = fr_dcursor_next(&cursor)) count++;
+
+               value = fr_value_box_alloc(ctx, FR_TYPE_UINT32, NULL, false);
+               value->datum.uint32 = count;
+               fr_dlist_insert_tail(out, value);
+               break;
+       }
+
+       /*
+        *      Output multiple #value_box_t, one per attribute.
+        */
+       case NUM_ALL:
+               if (!fr_dcursor_current(&cursor)) goto done;
+
+               /*
+                *      Loop over all matching #fr_value_pair
+                *      shallow copying buffers.
+                */
+               for (vp = fr_dcursor_current(&cursor);  /* Initialised above to the first matching attribute */
+                    vp;
+                    vp = fr_dcursor_next(&cursor)) {
+                       value = fr_value_box_alloc(ctx, vp->data.type, vp->da, vp->data.tainted);
+                       fr_value_box_copy(value, value, &vp->data);
+                       fr_dlist_insert_tail(out, value);
+               }
+               break;
+
+       default:
+               /*
+                *      The cursor was set to the correct
+                *      position above by tmpl_dcursor_init.
+                */
+               vp = fr_dcursor_current(&cursor);                       /* NULLness checked above */
+               value = fr_value_box_alloc(ctx, vp->data.type, vp->da, vp->data.tainted);
+               if (!value) goto oom;
+
+               fr_assert(fr_type_is_leaf(vp->da->type));
+               fr_value_box_copy(value, value, &vp->data);     /* Also dups taint */
+               fr_dlist_insert_tail(out, value);
+               break;
+       }
+
+done:
+       tmpl_dursor_clear(&cc);
+       return ret;
+}
+
+int tmpl_global_init(void)
+{
+       if (fr_dict_autoload(tmpl_dict) < 0) {
+               PERROR("%s", __FUNCTION__);
+               return -1;
+       }
+       if (fr_dict_attr_autoload(tmpl_dict_attr) < 0) {
+               PERROR("%s", __FUNCTION__);
+               fr_dict_autofree(tmpl_dict);
+               return -1;
+       }
+
+       return 0;
+}
+
+void tmpl_global_free(void)
+{
+       fr_dict_autofree(tmpl_dict);
+}
index 26d87613cbd43b35464feac994153ea71e0dfb10..bb3070a0e418f100a2e32e1652d2c70086fc4850 100644 (file)
@@ -46,38 +46,13 @@ static fr_dict_autoload_t xlat_eval_dict[] = {
        { NULL }
 };
 
-static fr_dict_attr_t const *attr_client_ip_address;
-static fr_dict_attr_t const *attr_client_shortname;
-static fr_dict_attr_t const *attr_packet_dst_ip_address;
-static fr_dict_attr_t const *attr_packet_dst_ipv6_address;
-static fr_dict_attr_t const *attr_packet_dst_port;
-static fr_dict_attr_t const *attr_packet_src_ip_address;
-static fr_dict_attr_t const *attr_packet_src_ipv6_address;
-static fr_dict_attr_t const *attr_packet_src_port;
-static fr_dict_attr_t const *attr_request_processing_stage;
-static fr_dict_attr_t const *attr_virtual_server;
-
-static fr_dict_attr_t const *attr_packet_authentication_vector;
-static fr_dict_attr_t const *attr_packet_type;
 fr_dict_attr_t const       *attr_expr_bool_enum; /* xlat_expr.c */
 fr_dict_attr_t const           *attr_module_return_code; /* xlat_expr.c */
 fr_dict_attr_t const           *attr_cast_base; /* xlat_expr.c */
 
 static fr_dict_attr_autoload_t xlat_eval_dict_attr[] = {
-       { .out = &attr_client_ip_address, .name = "Client-IP-Address", .type = FR_TYPE_IPV4_ADDR, .dict = &dict_freeradius },
-       { .out = &attr_client_shortname, .name = "Client-Shortname", .type = FR_TYPE_STRING, .dict = &dict_freeradius },
        { .out = &attr_module_return_code, .name = "Module-Return-Code", .type = FR_TYPE_UINT32, .dict = &dict_freeradius },
-       { .out = &attr_packet_dst_ip_address, .name = "Packet-Dst-IP-Address", .type = FR_TYPE_IPV4_ADDR, .dict = &dict_freeradius },
-       { .out = &attr_packet_dst_ipv6_address, .name = "Packet-Dst-IPV6-Address", .type = FR_TYPE_IPV6_ADDR, .dict = &dict_freeradius },
-       { .out = &attr_packet_dst_port, .name = "Packet-Dst-Port", .type = FR_TYPE_UINT16, .dict = &dict_freeradius },
-       { .out = &attr_packet_src_ip_address, .name = "Packet-Src-IP-Address", .type = FR_TYPE_IPV4_ADDR, .dict = &dict_freeradius },
-       { .out = &attr_packet_src_ipv6_address, .name = "Packet-Src-IPv6-Address", .type = FR_TYPE_IPV6_ADDR, .dict = &dict_freeradius },
-       { .out = &attr_packet_src_port, .name = "Packet-Src-Port", .type = FR_TYPE_UINT16, .dict = &dict_freeradius },
-       { .out = &attr_request_processing_stage, .name = "Request-Processing-Stage", .type = FR_TYPE_STRING, .dict = &dict_freeradius },
-       { .out = &attr_virtual_server, .name = "Virtual-Server", .type = FR_TYPE_STRING, .dict = &dict_freeradius },
-
-       { .out = &attr_packet_authentication_vector, .name = "Packet-Authentication-Vector", .type = FR_TYPE_OCTETS, .dict = &dict_radius },
-       { .out = &attr_packet_type, .name = "Packet-Type", .type = FR_TYPE_UINT32, .dict = &dict_radius },
+
        { .out = &attr_expr_bool_enum, .name = "Expr-Bool-Enum", .type = FR_TYPE_BOOL, .dict = &dict_freeradius },
        { .out = &attr_cast_base, .name = "Cast-Base", .type = FR_TYPE_UINT8, .dict = &dict_freeradius },
        { NULL }
@@ -690,277 +665,6 @@ xlat_action_t xlat_eval_one_letter(TALLOC_CTX *ctx, fr_value_box_list_t *out, re
        return XLAT_ACTION_DONE;
 }
 
-/** Gets the value of a virtual attribute
- *
- * These attribute *may* be overloaded by the user using real attribute.
- *
- * @todo There should be a virtual attribute registry.
- *
- * @param[in] ctx      to allocate boxed value, and buffers in.
- * @param[out] out     Where to write the boxed value.
- * @param[in] request  The current request.
- * @param[in] vpt      Representing the attribute.
- * @return
- *     - #XLAT_ACTION_FAIL     on memory allocation errors.
- *     - #XLAT_ACTION_DONE     if we're done processing this node.
- */
-static xlat_action_t xlat_eval_pair_virtual(TALLOC_CTX *ctx, fr_value_box_list_t *out,
-                                           request_t *request, tmpl_t const *vpt)
-{
-       fr_radius_packet_t      *packet = NULL;
-       fr_value_box_t  *value;
-
-       /*
-        *      Virtual attributes always have a count of 1
-        */
-       if (tmpl_num(vpt) == NUM_COUNT) {
-               MEM(value = fr_value_box_alloc(ctx, FR_TYPE_UINT32, NULL, false));
-               value->datum.uint32 = 1;
-               goto done;
-       }
-
-       /*
-        *      Some non-packet expansions
-        */
-       if (tmpl_da(vpt) == attr_client_shortname) {
-               RADCLIENT *client = client_from_request(request);
-               if (!client || !client->shortname) return XLAT_ACTION_DONE;
-
-               MEM(value = fr_value_box_alloc_null(ctx));
-               if (fr_value_box_bstrdup_buffer(ctx, value, tmpl_da(vpt), client->shortname, false) < 0) {
-               error:
-                       talloc_free(value);
-                       return XLAT_ACTION_FAIL;
-               }
-               goto done;
-       }
-
-       if (tmpl_da(vpt) == attr_request_processing_stage) {
-               if (!request->component) return XLAT_ACTION_DONE;
-
-               MEM(value = fr_value_box_alloc_null(ctx));
-               if (fr_value_box_strdup(ctx, value, tmpl_da(vpt), request->component, false) < 0) goto error;
-               goto done;
-       }
-
-       if (tmpl_da(vpt) == attr_virtual_server) {
-               if (!unlang_call_current(request)) return XLAT_ACTION_DONE;
-
-               MEM(value = fr_value_box_alloc_null(ctx));
-               if (fr_value_box_bstrdup_buffer(ctx, value, tmpl_da(vpt),
-                                              cf_section_name2(unlang_call_current(request)), false) < 0) goto error;
-               goto done;
-       }
-
-       if (tmpl_da(vpt) == attr_module_return_code) {
-               MEM(value = fr_value_box_alloc(ctx, tmpl_da(vpt)->type, tmpl_da(vpt), false));
-               value->datum.int32 = request->rcode;
-               goto done;
-       }
-
-       /*
-        *      All of the attributes must now refer to a packet.
-        *      If there's no packet, we can't print any attribute
-        *      referencing it.
-        */
-       packet = tmpl_packet_ptr(request, tmpl_list(vpt));
-       if (!packet) return XLAT_ACTION_DONE;
-
-       if (tmpl_da(vpt) == attr_packet_type) {
-               if (!packet || !packet->code) return XLAT_ACTION_DONE;
-
-               MEM(value = fr_value_box_alloc(ctx, tmpl_da(vpt)->type, NULL, false));
-               value->enumv = tmpl_da(vpt);
-               value->datum.int32 = packet->code;
-
-       /*
-        *      Virtual attributes which require a temporary fr_pair_t
-        *      to be allocated. We can't use stack allocated memory
-        *      because of the talloc checks sprinkled throughout the
-        *      various VP functions.
-        */
-       } else if (tmpl_da(vpt) == attr_packet_authentication_vector) {
-               MEM(value = fr_value_box_alloc_null(ctx));
-               fr_value_box_memdup(ctx, value, tmpl_da(vpt), packet->vector, sizeof(packet->vector), true);
-
-       } else if (tmpl_da(vpt) == attr_client_ip_address) {
-               RADCLIENT *client = client_from_request(request);
-               if (client) {
-                       MEM(value = fr_value_box_alloc_null(ctx));
-                       fr_value_box_ipaddr(value, NULL, &client->ipaddr, false);       /* Enum might not match type */
-                       goto done;
-               }
-               goto src_ip_address;
-
-       } else if (tmpl_da(vpt) == attr_packet_src_ip_address) {
-       src_ip_address:
-               if (!fr_socket_is_inet(packet->socket.proto) ||
-                   (packet->socket.inet.src_ipaddr.af != AF_INET)) return XLAT_ACTION_DONE;
-
-               MEM(value = fr_value_box_alloc_null(ctx));
-               fr_value_box_ipaddr(value, tmpl_da(vpt), &packet->socket.inet.src_ipaddr, true);
-
-       } else if (tmpl_da(vpt) == attr_packet_dst_ip_address) {
-               if (!fr_socket_is_inet(packet->socket.proto) ||
-                   (packet->socket.inet.dst_ipaddr.af != AF_INET)) return XLAT_ACTION_DONE;
-
-               MEM(value = fr_value_box_alloc_null(ctx));
-               fr_value_box_ipaddr(value, tmpl_da(vpt), &packet->socket.inet.dst_ipaddr, true);
-
-       } else if (tmpl_da(vpt) == attr_packet_src_ipv6_address) {
-               if (!fr_socket_is_inet(packet->socket.proto) ||
-                   (packet->socket.inet.src_ipaddr.af != AF_INET6)) return XLAT_ACTION_DONE;
-
-               MEM(value = fr_value_box_alloc_null(ctx));
-               fr_value_box_ipaddr(value, tmpl_da(vpt), &packet->socket.inet.src_ipaddr, true);
-
-       } else if (tmpl_da(vpt) == attr_packet_dst_ipv6_address) {
-               if (!fr_socket_is_inet(packet->socket.proto) ||
-                   (packet->socket.inet.dst_ipaddr.af != AF_INET6)) return XLAT_ACTION_DONE;
-
-               MEM(value = fr_value_box_alloc_null(ctx));
-               fr_value_box_ipaddr(value, tmpl_da(vpt), &packet->socket.inet.dst_ipaddr, true);
-
-       } else if (tmpl_da(vpt) == attr_packet_src_port) {
-               if (!fr_socket_is_inet(packet->socket.proto)) return XLAT_ACTION_DONE;
-
-               MEM(value = fr_value_box_alloc(ctx, tmpl_da(vpt)->type, NULL, true));
-               value->datum.uint16 = packet->socket.inet.src_port;
-
-       } else if (tmpl_da(vpt) == attr_packet_dst_port) {
-               if (!fr_socket_is_inet(packet->socket.proto)) return XLAT_ACTION_DONE;
-
-               MEM(value = fr_value_box_alloc(ctx, tmpl_da(vpt)->type, NULL, true));
-               value->datum.uint16 = packet->socket.inet.dst_port;
-
-       } else {
-               RERROR("Attribute \"%s\" incorrectly marked as virtual", tmpl_da(vpt)->name);
-               return XLAT_ACTION_FAIL;
-       }
-
-done:
-       fr_dlist_insert_tail(out, value);
-
-       return XLAT_ACTION_DONE;
-}
-
-
-/** Gets the value of a real or virtual attribute
- *
- * @param[in] ctx      to allocate boxed value, and buffers in.
- * @param[out] out     Where to write the boxed value.
- * @param[in] request  The current request.
- * @param[in] vpt      Representing the attribute.
- * @return
- *     - #XLAT_ACTION_FAIL             we failed getting a value for the attribute.
- *     - #XLAT_ACTION_DONE             we successfully evaluated the xlat.
- */
-static inline CC_HINT(always_inline) xlat_action_t
-xlat_eval_pair_real(TALLOC_CTX *ctx, fr_value_box_list_t *out, request_t *request, tmpl_t const *vpt)
-{
-       fr_pair_t               *vp = NULL;
-       fr_value_box_t          *value;
-
-       fr_dcursor_t            cursor;
-       tmpl_dcursor_ctx_t      cc;
-
-       xlat_action_t           ret = XLAT_ACTION_DONE;
-
-       fr_assert(tmpl_is_attr(vpt) || tmpl_is_list(vpt));
-
-       /*
-        *      See if we're dealing with an attribute in the request
-        *
-        *      This allows users to manipulate virtual attributes as if
-        *      they were real ones.
-        */
-       vp = tmpl_dcursor_init(NULL, NULL, &cc, &cursor, request, vpt);
-
-       /*
-        *      We didn't find the VP in a list, check to see if it's
-        *      virtual.
-        */
-       if (!vp) {
-               if (tmpl_is_attr(vpt) && tmpl_da(vpt)->flags.virtual) {
-                       ret = xlat_eval_pair_virtual(ctx, out, request, vpt);
-                       goto done;
-               }
-
-               /*
-                *      Zero count.
-                */
-               if (tmpl_num(vpt) == NUM_COUNT) {
-                       value = fr_value_box_alloc(ctx, FR_TYPE_UINT32, NULL, false);
-                       if (!value) {
-                       oom:
-                               fr_strerror_const("Out of memory");
-                               ret = XLAT_ACTION_FAIL;
-                               goto done;
-                       }
-                       value->datum.int32 = 0;
-                       fr_dlist_insert_tail(out, value);
-               } /* Fall through to being done */
-
-               goto done;
-       }
-
-       switch (tmpl_num(vpt)) {
-       /*
-        *      Return a count of the VPs.
-        */
-       case NUM_COUNT:
-       {
-               uint32_t                count = 0;
-
-               for (vp = fr_dcursor_current(&cursor);
-                    vp;
-                    vp = fr_dcursor_next(&cursor)) count++;
-
-               value = fr_value_box_alloc(ctx, FR_TYPE_UINT32, NULL, false);
-               value->datum.uint32 = count;
-               fr_dlist_insert_tail(out, value);
-               break;
-       }
-
-       /*
-        *      Output multiple #value_box_t, one per attribute.
-        */
-       case NUM_ALL:
-               if (!fr_dcursor_current(&cursor)) goto done;
-
-               /*
-                *      Loop over all matching #fr_value_pair
-                *      shallow copying buffers.
-                */
-               for (vp = fr_dcursor_current(&cursor);  /* Initialised above to the first matching attribute */
-                    vp;
-                    vp = fr_dcursor_next(&cursor)) {
-                       value = fr_value_box_alloc(ctx, vp->data.type, vp->da, vp->data.tainted);
-                       fr_value_box_copy(value, value, &vp->data);
-                       fr_dlist_insert_tail(out, value);
-               }
-               break;
-
-       default:
-               /*
-                *      The cursor was set to the correct
-                *      position above by tmpl_dcursor_init.
-                */
-               vp = fr_dcursor_current(&cursor);                       /* NULLness checked above */
-               value = fr_value_box_alloc(ctx, vp->data.type, vp->da, vp->data.tainted);
-               if (!value) goto oom;
-
-               fr_assert(fr_type_is_leaf(vp->da->type));
-               fr_value_box_copy(value, value, &vp->data);     /* Also dups taint */
-               fr_dlist_insert_tail(out, value);
-               break;
-       }
-
-done:
-       tmpl_dursor_clear(&cc);
-       return ret;
-}
-
 typedef struct {
        int                     status;
        fr_value_box_list_t     list;
@@ -1344,7 +1048,7 @@ xlat_action_t xlat_frame_eval(TALLOC_CTX *ctx, fr_dcursor_t *out, xlat_exp_head_
                                }
                                xlat_debug_log_expansion(request, node, NULL);
 
-                               if (xlat_eval_pair_real(ctx, &result, request, node->vpt) == XLAT_ACTION_FAIL) goto fail;
+                               if (tmpl_eval_pair(ctx, &result, request, node->vpt) < 0) goto fail;
 
                        } else if (tmpl_is_exec(node->vpt)) { /* exec only */
                                xlat_exec_rctx_t *rctx;