** xref:keywords.adoc[Keywords]
*** xref:break.adoc[break]
*** xref:call.adoc[call]
+*** xref:caller.adoc[caller]
*** xref:case.adoc[case]
*** xref:detach.adoc[detach]
*** xref:else.adoc[else]
--- /dev/null
+= The caller Statement
+
+.Syntax
+[source,unlang]
+----
+caller <protocol> {
+ [ statements ]
+}
+----
+
+The `caller` keyword checks the parent request to see if it matches a
+particular protocol. If so, it executes the given _[statements]_, If
+the parent does not exist, or the parent protocol does not match, the
+statements are ignored.
+
+<protocol>:: The name of the protocol used by the virtual server which `call`ed this policy.
+
+[ statements ]:: If the protocol matches, the _[
+statements]_ from inside of the `caller` section will be executed.
+These statements will be executed in the context of the current request.
+
+A virtual server may the the target of multiple xref:call.adoc[call]
+keywords, each of which uses a different protocol. For example, a
+policy which handles RADIUS `Access-Request` packets may create a DHCP
+`Discover` packet in order to perform IP address allocation. The DHCP
+`Discover` packet will typically be processed through `DHCPv4` virtual
+server. However, that virtual server may need to handle requests
+differently based on whether the original packet was RADIUS, or
+DHCPv4.
+
+The `caller` keyword allows a virtual server to check where it is
+called from, and to apply different policies based on that
+information. The `caller` keywords is in some senses the counter-part
+to the xref:call.adoc[call] keyword.
+
+It may have been possible to use `if` conditions to perform this
+check. However, doing so would involve creating additional syntax
+which would not have been as clear as using `caller`.
+
+.Example
+
+This example copies the `DHCP-Client-Identifer` from the parent DHCPv4
+request to the current RADIUS reply packet, as the contents of the
+`Filter-Id` attribute.
+
+[source,unlang]
+----
+caller dhcpv4 {
+ update reply {
+ &Filter-Id := &parent.request:DHCP-Client-Identifier
+ }
+}
+----
+
+// Copyright (C) 2020 Network RADIUS SAS. Licenced under CC-by-NC 4.0.
+// Development of this documentation was sponsored by Network RADIUS SAS.
|=====
| Keyword | Description
| xref:call.adoc[call] | Call one virtual server from another
+| xref:caller.adoc[caller] | Check if the request is from a parent `call`
| xref:detach.adoc[detach] | Detach a child from a parent
| xref:parallel.adoc[parallel] | Create multiple parallel child requests
| xref:subrequest.adoc[subrequest] | Create a child request
SOURCES := base.c \
call.c \
+ caller.c \
compile.c \
condition.c \
foreach.c \
if (unlang_subrequest_op_init() < 0) return -1;
unlang_switch_init();
unlang_call_init();
+ unlang_caller_init();
unlang_tmpl_init_shallow();
return 0;
--- /dev/null
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/**
+ * $Id$
+ *
+ * @file unlang/caller.c
+ * @brief Unlang "caller" keyword evaluation. Used for setting allowed parent protocols
+ *
+ * @copyright 2020 The FreeRADIUS server project
+ */
+RCSID("$Id$")
+
+#include <freeradius-devel/server/state.h>
+#include "unlang_priv.h"
+#include "group_priv.h"
+
+static unlang_action_t unlang_caller(REQUEST *request, rlm_rcode_t *presult)
+{
+ unlang_stack_t *stack = request->stack;
+ unlang_stack_frame_t *frame = &stack->frame[stack->depth];
+ unlang_t *instruction = frame->instruction;
+
+ unlang_group_t *g;
+
+ g = unlang_generic_to_group(instruction);
+ fr_assert(g->num_children > 0); /* otherwise the compilation is broken */
+
+ /*
+ * No parent, or the dictionaries don't match. Ignore it.
+ */
+ if (!request->parent || (request->parent->dict != g->dict)) {
+ RDEBUG2("...");
+ return UNLANG_ACTION_EXECUTE_NEXT;
+ }
+
+ /*
+ * The dictionary matches. Go recurse into its' children.
+ */
+ return unlang_group(request, presult);
+}
+
+
+void unlang_caller_init(void)
+{
+ unlang_register(UNLANG_TYPE_CALLER,
+ &(unlang_op_t){
+ .name = "caller",
+ .interpret = unlang_caller,
+ .debug_braces = true
+ });
+}
break;
case UNLANG_TYPE_CALL:
+ case UNLANG_TYPE_CALLER:
case UNLANG_TYPE_CASE:
case UNLANG_TYPE_FOREACH:
case UNLANG_TYPE_ELSE:
}
+static unlang_t *compile_caller(unlang_t *parent, unlang_compile_t *unlang_ctx, CONF_SECTION *cs)
+{
+ unlang_t *c;
+ unlang_group_t *g;
+ fr_token_t type;
+ char const *name;
+ fr_dict_t const *dict;
+
+ name = cf_section_name2(cs);
+ if (!name) {
+ cf_log_err(cs, "You MUST specify a protocol name for 'caller <protocol> { ... }'");
+ return NULL;
+ }
+
+ type = cf_section_name2_quote(cs);
+ if (type != T_BARE_WORD) {
+ cf_log_err(cs, "The argument to 'caller' cannot be a quoted string or a dynamic value");
+ return NULL;
+ }
+
+ dict = fr_dict_by_protocol_name(name);
+ if (!dict) {
+ cf_log_err(cs, "Unknown protocol '%s'", name);
+ return NULL;
+ }
+
+ c = compile_section(parent, unlang_ctx, cs, UNLANG_TYPE_CALLER);
+ if (!c) return NULL;
+
+ /*
+ * Set the virtual server name, which tells unlang_call()
+ * which virtual server to call.
+ */
+ g = unlang_generic_to_group(c);
+ g->dict = dict;
+
+ if (!g->num_children) {
+ talloc_free(c);
+ return UNLANG_IGNORE;
+ }
+
+ return c;
+}
+
static unlang_t *compile_function(unlang_t *parent, unlang_compile_t *unlang_ctx, CONF_ITEM *ci,
CONF_SECTION *subcs, rlm_components_t component,
bool policy)
static fr_table_ptr_sorted_t unlang_section_keywords[] = {
{ L("call"), (void *) compile_call },
+ { L("caller"), (void *) compile_caller },
{ L("case"), (void *) compile_case },
{ L("else"), (void *) compile_else },
{ L("elsif"), (void *) compile_elsif },
UNLANG_TYPE_SUBREQUEST, //!< create a child subrequest
UNLANG_TYPE_DETACH, //!< detach a child
UNLANG_TYPE_CALL, //!< call another virtual server
+ UNLANG_TYPE_CALLER, //!< conditionally check parent dictionary type
UNLANG_TYPE_POLICY, //!< Policy section.
UNLANG_TYPE_XLAT, //!< Represents one level of an xlat expansion.
UNLANG_TYPE_TMPL, //!< asynchronously expand a tmpl_t
*/
void unlang_call_init(void);
+void unlang_caller_init(void);
+
void unlang_condition_init(void);
void unlang_foreach_init(void);