}
recv {
update request {
- &Acct-Delay-Time := "%{expr:%{%{Acct-Delay-Time}:-0} + %c - %{%{integer:Event-Timestamp}:-%{integer:Packet-Original-Timestamp}}}"
+ &Acct-Delay-Time := "%{expr:%{%{Acct-Delay-Time}:-0} + %c - %{%(integer:%{Event-Timestamp}):-%(integer:%{Packet-Original-Timestamp})}}"
}
ok
}
....
====
-=== %{integer:<&ref>}
+=== %(integer:<value>)
-Print the value of the attribute an integer.
+Convert a value to an integer.
In normal operation, `integer` attributes are printed using the name
given by a `VALUE` statement in a dictionary. Similarly, date
A common usage is to find the difference between two dates.
For example, if a request contains `Service-Type = Login-User`, the
-expansion of `%{integer:&Service-Type}` will yield `1`, which is the
+expansion of `%(integer:%{Service-Type})` will yield `1`, which is the
value associated with the `Login-User` name. Using
-`%{integer:&Event-Timestamp}` will return the event timestamp as an
+`%(integer:%{Event-Timestamp})` will return the event timestamp as an
unsigned 32-bit number.
.Return: _string_
&control.Service-Type := Login-User
}
update reply {
- &Reply-Message := "The value of Service-Type is %{integer:&control.Service-Type}"
+ &Reply-Message := "The value of Service-Type is %(integer:%{control.Service-Type})"
}
----
The "string" is expanded before the length is taken.
-.IP %{integer:Attribute-Name}
-The integer value of the Attribute-Name, instead of the enumerated
-name.
+.IP %(integer:Value)
+The integer value of the Value.
e.g. If a request contains "Service-Type = Login-User", the expansion
-of %{integer:Service-Type} will yield "1".
+of %(integer:%{Service-Type}) will yield "1".
.IP %{hex:Attribute-Name}
The hex value of the Attribute-Name, as a series of hex digits.
# distrust the provided Event-Timestamp.
#event_timestamp_epoch = "%l"
-event_timestamp_epoch = "%{%{integer:Event-Timestamp}:-%l}"
+event_timestamp_epoch = "%{%(integer:%{Event-Timestamp}):-%l}"
# event_timestamp is the SQL snippet for converting an epoch timestamp
# to an SQL date.
# distrust the provided Event-Timestamp.
#event_timestamp_epoch = "%l"
-event_timestamp_epoch = "%{%{integer:Event-Timestamp}:-%l}"
+event_timestamp_epoch = "%{%(integer:%{Event-Timestamp}):-%l}"
# event_timestamp is the SQL snippet for converting an epoch timestamp
# to an SQL date.
# distrust the provided Event-Timestamp.
#event_timestamp_epoch = "%l"
-event_timestamp_epoch = "%{%{integer:Event-Timestamp}:-%l}"
+event_timestamp_epoch = "%{%(integer:%{Event-Timestamp}):-%l}"
# event_timestamp is the SQL snippet for converting an epoch timestamp
# to an SQL date.
# distrust the provided Event-Timestamp.
#event_timestamp_epoch = "%l"
-event_timestamp_epoch = "%{%{integer:Event-Timestamp}:-%l}"
+event_timestamp_epoch = "%{%(integer:%{Event-Timestamp}):-%l}"
# event_timestamp is the SQL snippet for converting an epoch timestamp
# to an SQL date.
# distrust the provided Event-Timestamp.
#event_timestamp_epoch = "%l"
-event_timestamp_epoch = "%{%{integer:Event-Timestamp}:-%l}"
+event_timestamp_epoch = "%{%(integer:%{Event-Timestamp}):-%l}"
# event_timestamp is the SQL snippet for converting an epoch timestamp
# to an SQL date.
# distrust the provided Event-Timestamp.
#event_timestamp_epoch = "%l"
-event_timestamp_epoch = "%{%{integer:Event-Timestamp}:-%l}"
+event_timestamp_epoch = "%{%(integer:%{Event-Timestamp}):-%l}"
# event_timestamp is the SQL snippet for converting an epoch timestamp
# to an SQL date.
#
recv Accounting-Request {
update request {
- &Acct-Delay-Time := "%{expr:%{%{Acct-Delay-Time}:-0} + %c - %{%{integer:Event-Timestamp}:-%{integer:Packet-Original-Timestamp}}}"
+ &Acct-Delay-Time := "%{expr:%{%{Acct-Delay-Time}:-0} + %c - %{%(integer:%{Event-Timestamp}):-%(integer:%{Packet-Original-Timestamp})}}"
}
#
return XLAT_ACTION_DONE;
}
+static xlat_arg_parser_t const xlat_func_integer_args[] = {
+ { .required = true, .single = true, .type = FR_TYPE_VOID },
+ XLAT_ARG_PARSER_TERMINATOR
+};
+
/** Print data as integer, not as VALUE.
*
* Example:
update request {
&Tmp-IP-Address-0 := "127.0.0.5"
}
-"%{integer:&Tmp-IP-Address-0}" == 2130706437
+"%(integer:%{Tmp-IP-Address-0})" == 2130706437
@endverbatim
* @ingroup xlat_functions
*/
-static ssize_t xlat_func_integer(UNUSED TALLOC_CTX *ctx, char **out, size_t outlen,
- UNUSED void const *mod_inst, UNUSED void const *xlat_inst,
- request_t *request, char const *fmt)
+static xlat_action_t xlat_func_integer(TALLOC_CTX *ctx, fr_dcursor_t *out,
+ request_t *request, UNUSED void const *xlat_inst,
+ UNUSED void *xlat_thread_inst, fr_value_box_list_t *in)
{
- fr_pair_t *vp;
+ fr_value_box_t *in_vb = fr_dlist_head(in);
- uint64_t int64 = 0; /* Needs to be initialised to zero */
- uint32_t int32 = 0; /* Needs to be initialised to zero */
-
- fr_skip_whitespace(fmt);
-
- if ((xlat_fmt_get_vp(&vp, request, fmt) < 0) || !vp) return 0;
+ fr_strerror_clear(); /* Make sure we don't print old errors */
+
+ switch (in_vb->type) {
+ default:
+ error:
+ RPEDEBUG("Failed converting %pV (%s) to an integer", in_vb,
+ fr_table_str_by_value(fr_value_box_type_table, in_vb->type, "???"));
+ return XLAT_ACTION_FAIL;
+
+ case FR_TYPE_NUMERIC:
+ /*
+ * Ensure enumeration is NULL so that the integer
+ * version of a box is returned
+ */
+ in_vb->enumv = NULL;
+
+ /*
+ * FR_TYPE_DATE and FR_TYPE_DELTA need to be cast to
+ * uin64_t so they're printed in a numeric format.
+ */
+ if ((in_vb->type != FR_TYPE_DATE) || (in_vb->type != FR_TYPE_TIME_DELTA)) break;
+ FALL_THROUGH;
- switch (vp->vp_type) {
- case FR_TYPE_DATE:
case FR_TYPE_STRING:
- {
- fr_value_box_t vb;
-
- if (fr_value_box_cast(NULL, &vb, FR_TYPE_UINT64, NULL, &vp->data) < 0) {
- RPEDEBUG("Invalid input for printing as an integer");
- return -1;
- }
-
- return snprintf(*out, outlen, "%" PRIu64, vb.vb_uint64);
- }
+ if (fr_value_box_cast_in_place(ctx, in_vb, FR_TYPE_UINT64, NULL) < 0) goto error;
+ break;
case FR_TYPE_OCTETS:
- if (vp->vp_length > 8) {
- break;
- }
+ if (in_vb->vb_length > sizeof(uint64_t)) goto error;
- if (vp->vp_length > 4) {
- memcpy(&int64, vp->vp_octets, vp->vp_length);
- return snprintf(*out, outlen, "%" PRIu64, htonll(int64));
+ if (in_vb->vb_length > sizeof(uint32_t)) {
+ fr_value_box_cast_in_place(ctx, in_vb, FR_TYPE_UINT64, NULL);
+ break;
}
- memcpy(&int32, vp->vp_octets, vp->vp_length);
- return snprintf(*out, outlen, "%i", htonl(int32));
-
- case FR_TYPE_UINT64:
- return snprintf(*out, outlen, "%" PRIu64, vp->vp_uint64);
+ fr_value_box_cast_in_place(ctx, in_vb, FR_TYPE_UINT32, NULL);
+ break;
- /*
- * IP addresses are treated specially, as parsing functions assume the value
- * is bigendian and will convert it for us.
- */
case FR_TYPE_IPV4_ADDR:
- case FR_TYPE_IPV4_PREFIX: /* Same addr field */
- return snprintf(*out, outlen, "%u", ntohl(vp->vp_ipv4addr));
-
- case FR_TYPE_UINT32:
- return snprintf(*out, outlen, "%u", vp->vp_uint32);
-
- case FR_TYPE_UINT8:
- return snprintf(*out, outlen, "%u", (unsigned int) vp->vp_uint8);
-
- case FR_TYPE_UINT16:
- return snprintf(*out, outlen, "%u", (unsigned int) vp->vp_uint16);
+ case FR_TYPE_IPV4_PREFIX:
+ if (fr_value_box_cast_in_place(ctx, in_vb, FR_TYPE_UINT32, NULL) < 0) goto error;
+ break;
- /*
- * Ethernet is weird... It's network related, so it
- * should be bigendian.
- */
case FR_TYPE_ETHERNET:
- int64 = vp->vp_ether[0];
- int64 <<= 8;
- int64 |= vp->vp_ether[1];
- int64 <<= 8;
- int64 |= vp->vp_ether[2];
- int64 <<= 8;
- int64 |= vp->vp_ether[3];
- int64 <<= 8;
- int64 |= vp->vp_ether[4];
- int64 <<= 8;
- int64 |= vp->vp_ether[5];
- return snprintf(*out, outlen, "%" PRIu64, int64);
-
- case FR_TYPE_INT32:
- return snprintf(*out, outlen, "%i", vp->vp_int32);
+ if (fr_value_box_cast_in_place(ctx, in_vb, FR_TYPE_UINT64, NULL) < 0) goto error;
+ break;
case FR_TYPE_IPV6_ADDR:
case FR_TYPE_IPV6_PREFIX:
{
- uint128_t ipv6int;
+ uint128_t ipv6int;
+ char buff[40];
+ fr_value_box_t *vb;
/*
* Needed for correct alignment (as flagged by ubsan)
*/
- memcpy(&ipv6int, &vp->vp_ipv6addr, sizeof(ipv6int));
+ memcpy(&ipv6int, &in_vb->vb_ip.addr.v6.s6_addr, sizeof(ipv6int));
- return fr_snprint_uint128(*out, outlen, ntohlll(ipv6int));
- }
+ fr_snprint_uint128(buff, sizeof(buff), ntohlll(ipv6int));
- default:
- break;
+ MEM(vb = fr_value_box_alloc_null(ctx));
+ fr_value_box_bstrndup(ctx, vb, NULL, buff, strlen(buff), false);
+ fr_dcursor_append(out, vb);
+ return XLAT_ACTION_DONE;
+ }
}
- REDEBUG("Type '%s' cannot be converted to integer", fr_table_str_by_value(fr_value_box_type_table, vp->vp_type, "???"));
+ fr_dlist_remove(in, in_vb);
+ fr_dcursor_append(out, in_vb);
- return -1;
+ return XLAT_ACTION_DONE;
}
static xlat_arg_parser_t const xlat_func_map_arg = {
#define XLAT_REGISTER(_x) xlat = xlat_register_legacy(NULL, STRINGIFY(_x), xlat_func_ ## _x, NULL, NULL, 0, XLAT_DEFAULT_BUF_LEN); \
xlat_internal(xlat);
- XLAT_REGISTER(integer);
XLAT_REGISTER(map);
XLAT_REGISTER(xlat);
XLAT_REGISTER_ARGS("explode", xlat_func_explode, xlat_func_explode_args);
XLAT_REGISTER_ARGS("hmacmd5", xlat_func_hmac_md5, xlat_hmac_args);
XLAT_REGISTER_ARGS("hmacsha1", xlat_func_hmac_sha1, xlat_hmac_args);
+ XLAT_REGISTER_ARGS("integer", xlat_func_integer, xlat_func_integer_args);
XLAT_REGISTER_ARGS("join", xlat_func_join, xlat_func_join_args);
XLAT_REGISTER_ARGS("nexttime", xlat_func_next_time, xlat_func_next_time_args);
XLAT_REGISTER_ARGS("pairs", xlat_func_pairs, xlat_func_pairs_args);
fr_sbuff_t *in, bool brace,
fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules);
+static inline int xlat_tokenize_function_args(TALLOC_CTX *ctx, xlat_exp_t **head,
+ xlat_flags_t *flags, fr_sbuff_t *in,
+ tmpl_rules_t const *rules);
+
static inline int xlat_tokenize_alternation(TALLOC_CTX *ctx, xlat_exp_t **head, xlat_flags_t *flags, fr_sbuff_t *in,
- tmpl_rules_t const *t_rules)
+ tmpl_rules_t const *t_rules, bool func_args)
{
xlat_exp_t *node;
node = xlat_exp_alloc_null(ctx);
xlat_exp_set_type(node, XLAT_ALTERNATE);
- if (xlat_tokenize_expansion(node, &node->child, &node->flags, in, t_rules) < 0) {
- error:
- *head = NULL;
- talloc_free(node);
- return -1;
+
+ if (func_args) {
+ if (xlat_tokenize_function_args(node, &node->child, &node->flags, in, t_rules) < 0) {
+ error:
+ *head = NULL;
+ talloc_free(node);
+ return -1;
+ }
+ } else {
+ if (xlat_tokenize_expansion(node, &node->child, &node->flags, in, t_rules) < 0) goto error;
}
if (!fr_sbuff_adv_past_str_literal(in, ":-")) {
XLAT_DEBUG("FUNC <-- %pV", fr_box_strvalue_len(fr_sbuff_current(in), fr_sbuff_remaining(in)));
/*
- * %{module:args}
+ * %(module:args)
*/
fr_sbuff_marker(&m_s, in);
fr_sbuff_adv_past_allowed(in, SIZE_MAX, func_chars, NULL);
* %{...}:-bar}
*/
if (fr_sbuff_adv_past_str_literal(in, "%{")) {
- return xlat_tokenize_alternation(ctx, head, flags, in, t_rules);
+ return xlat_tokenize_alternation(ctx, head, flags, in, t_rules, false);
+ }
+
+ /*
+ * %(...):-bar}
+ */
+ if (fr_sbuff_adv_past_str_literal(in, "%(")) {
+ return xlat_tokenize_alternation(ctx, head, flags, in, t_rules, true);
}
/*
return fr_value_box_cast_integer_to_integer(ctx, dst, dst_type, dst_enumv, src);
case FR_TYPE_IPV4_ADDR:
+ case FR_TYPE_IPV4_PREFIX:
{
fr_value_box_t tmp;
}
fr_value_box_init(&tmp, FR_TYPE_UINT64, src->enumv, src->tainted);
-#ifdef WORDS_BIGENDIAN
memcpy(((uint8_t *)&tmp.vb_uint64) + (sizeof(tmp.vb_uint64) - sizeof(src->vb_ether)),
&src->vb_ether, sizeof(src->vb_ether));
-#else
- memcpy(&tmp.vb_uint64, &src->vb_ether, sizeof(tmp.vb_ether));
-#endif
+#ifndef WORDS_BIGENDIAN
+ /*
+ * Ethernet addresses are always stored bigendian,
+ * convert to native on little endian systems
+ */
fr_value_box_hton(&tmp, &tmp);
+#endif
return fr_value_box_cast_integer_to_integer(ctx, dst, dst_type, dst_enumv, &tmp);
}
# String - network order representation of a 4 char string
update request {
- &Tmp-Integer-1 := "%{integer:Tmp-String-0}"
+ &Tmp-Integer-1 := "%(integer:%{Tmp-String-0})"
}
if ((<integer>&Tmp-String-0 != &Tmp-Integer-1) || (&Tmp-Integer-1 != 9870)) {
test_fail
# String - network order representation of a 8 char string
update request {
- &Tmp-Integer64-0 := "%{integer:Tmp-String-1}"
+ &Tmp-Integer64-0 := "%(integer:%{Tmp-String-1})"
}
if ((<integer64>&Tmp-String-1 != &Tmp-Integer64-0) || (&Tmp-Integer64-0 != 98709870)) {
test_fail
}
# String - Can't convert 12 byte string to integer (our biggest native size is a 64bit unsigned int)
-if ("%{integer:Tmp-String-2}" != '') {
+if ("%(integer:%{Tmp-String-2})" != '') {
test_fail
}
# Octets - network order representation of a 4 byte octet string
update request {
- &Tmp-Integer-1 := "%{integer:Tmp-Octets-0}"
+ &Tmp-Integer-1 := "%(integer:%{Tmp-Octets-0})"
}
#
# Octets - network order representation of a 8 byte octet string
update request {
- &Tmp-Integer64-0 := "%{integer:Tmp-Octets-1}"
+ &Tmp-Integer64-0 := "%(integer:%{Tmp-Octets-1})"
}
#
}
# String - Can't convert 12 byte octet string to integer (our biggest native size is a 64bit unsigned int)
-if ("%{integer:Tmp-Octets-2}" != '') {
+if ("%(integer:%{Tmp-Octets-2})" != '') {
test_fail
}
update request {
- &Tmp-String-2 := "%{integer:Tmp-IP-Address-0}"
- &Tmp-String-3 := "%{integer:Tmp-Date-0}"
- &Tmp-String-4 := "%{integer:Tmp-Integer-0}"
- &Tmp-String-6 := "%{integer:Tmp-Cast-Ifid}"
- &Tmp-String-7 := "%{integer:Tmp-Cast-IPv6Addr}"
- &Tmp-String-8 := "%{integer:Tmp-Cast-IPv6Prefix}"
+ &Tmp-String-2 := "%(integer:%{Tmp-IP-Address-0})"
+ &Tmp-String-3 := "%(integer:%{Tmp-Date-0})"
+ &Tmp-String-4 := "%(integer:%{Tmp-Integer-0})"
+ &Tmp-String-6 := "%(integer:%{Tmp-Cast-Ifid})"
+ &Tmp-String-7 := "%(integer:%{Tmp-Cast-IPv6Addr})"
+ &Tmp-String-8 := "%(integer:%{Tmp-Cast-IPv6Prefix})"
}
# IP Address
test_fail
}
update request {
- &Tmp-String-0 := "%{integer:Tmp-Cast-Byte}"
- &Tmp-String-1 := "%{integer:Tmp-Cast-Short}"
- &Tmp-Integer64-2 := "%{integer:Tmp-Cast-Ether}"
- &Tmp-String-3 := "%{integer:Tmp-Cast-Integer64}"
- &Tmp-String-4 := "%{integer:Tmp-Cast-IPv4Prefix}"
+ &Tmp-String-0 := "%(integer:%{Tmp-Cast-Byte})"
+ &Tmp-String-1 := "%(integer:%{Tmp-Cast-Short})"
+ &Tmp-Integer64-2 := "%(integer:%{Tmp-Cast-Ether})"
+ &Tmp-String-3 := "%(integer:%{Tmp-Cast-Integer64})"
+ &Tmp-String-4 := "%(integer:%{Tmp-Cast-IPv4Prefix})"
}
# byte
test_fail
}
+update request {
+ &Service-Type := Login-User
+}
+
+update request {
+ &Tmp-Integer-2 := "%(integer:%{Service-Type})"
+}
+
+if (&Tmp-Integer-2 != 1) {
+ test_fail
+}
+
success
&Tmp-Date-0 := "%l"
}
-if ("%{expr:%{redis:ZSCORE '{%{control.IP-Pool.Name}%}:pool' '%{reply.Framed-IP-Address}'} - %{integer:&Tmp-Date-0}}" > 20) {
+if ("%{expr:%{redis:ZSCORE '{%{control.IP-Pool.Name}%}:pool' '%{reply.Framed-IP-Address}'} - %(integer:%{Tmp-Date-0})}" > 20) {
test_pass
} else {
test_fail
}
-if ("%{expr:%{redis:ZSCORE '{%{control.IP-Pool.Name}%}:pool' '%{reply.Framed-IP-Address}'} - %{integer:&Tmp-Date-0}}" < 40) {
+if ("%{expr:%{redis:ZSCORE '{%{control.IP-Pool.Name}%}:pool' '%{reply.Framed-IP-Address}'} - %(integer:%{Tmp-Date-0})}" < 40) {
test_pass
} else {
test_fail
&Tmp-Date-0 := "%l"
}
-if ("%{expr:%{redis:ZSCORE '{%{control.IP-Pool.Name}%}:pool' '%{reply.Framed-IP-Address}'} - %{integer:&Tmp-Date-0}}" > 0) {
+if ("%{expr:%{redis:ZSCORE '{%{control.IP-Pool.Name}%}:pool' '%{reply.Framed-IP-Address}'} - %(integer:%{Tmp-Date-0})}" > 0) {
test_pass
} else {
test_fail
}
-if ("%{expr:%{redis:ZSCORE '{%{control.IP-Pool.Name}%}:pool' '%{reply.Framed-IP-Address}'} - %{integer:&Tmp-Date-0}}" < 10) {
+if ("%{expr:%{redis:ZSCORE '{%{control.IP-Pool.Name}%}:pool' '%{reply.Framed-IP-Address}'} - %(integer:%{Tmp-Date-0})}" < 10) {
test_pass
} else {
test_fail
&Tmp-Date-0 := "%l"
}
-if ("%{expr:%{redis:ZSCORE '{%{control.IP-Pool.Name}%}:pool' '%{reply.Framed-IP-Address}'} - %{integer:&Tmp-Date-0}}" > 50) {
+if ("%{expr:%{redis:ZSCORE '{%{control.IP-Pool.Name}%}:pool' '%{reply.Framed-IP-Address}'} - %(integer:%{Tmp-Date-0})}" > 50) {
test_pass
} else {
test_fail
}
# 9.
-if ("%{expr:%{redis:ZSCORE '{%{control.IP-Pool.Name}%}:pool' '%{reply.Framed-IP-Address}'} - %{integer:&Tmp-Date-0}}" < 70) {
+if ("%{expr:%{redis:ZSCORE '{%{control.IP-Pool.Name}%}:pool' '%{reply.Framed-IP-Address}'} - %(integer:%{Tmp-Date-0})}" < 70) {
test_pass
} else {
test_fail