The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
load_balance.c
Go to the documentation of this file.
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17/**
18 * $Id: 93a27d43d786d2f4928dff836f9cb0515da3c68e $
19 *
20 * @file unlang/load_balance.c
21 * @brief Implementation of the unlang "load-balance" keyword.
22 *
23 * @copyright 2006-2019 The FreeRADIUS server project
24 */
25#include <freeradius-devel/server/rcode.h>
26#include <freeradius-devel/util/hash.h>
27#include <freeradius-devel/util/rand.h>
28
29#include "unlang_priv.h"
30#include "load_balance_priv.h"
31
32#define unlang_redundant_load_balance unlang_load_balance
33
36{
37 unlang_frame_state_redundant_t *redundant = talloc_get_type_abort(frame->state, unlang_frame_state_redundant_t);
39
40 /*
41 * If the current child wasn't set, we just start there. Otherwise we loop around to the next
42 * child.
43 */
44 if (!redundant->child) {
45 redundant->child = redundant->start;
46 goto push;
47 }
48
49 /*
50 * We are in a resumed frame. Check if running the child resulting in an rcode that we can use.
51 * If so, stop.
52 */
53 if ((redundant->result.rcode != RLM_MODULE_NOT_SET) &&
54 (redundant->child->actions.actions[redundant->result.rcode] == MOD_ACTION_RETURN)) {
55 if (p_result) {
56 p_result->priority = MOD_PRIORITY_MIN;
57 p_result->rcode = redundant->result.rcode;
58 }
59
61 }
62
63 /*
64 * We finished the previous child, and it failed. Go to the next one. If we fall off of the
65 * end, loop around to the next one.
66 */
67 redundant->child = redundant->child->next;
68 if (!redundant->child) redundant->child = g->children;
69
70 /*
71 * We looped back to the start. Return whatever results we had from the last child.
72 */
73 if (redundant->child == redundant->start) {
74 if (p_result) *p_result = redundant->result;
76 }
77
78push:
79 /*
80 * The child begins has no result set. Resetting the results ensures that the failed code from
81 * one child doesn't affect the next child that we run.
82 */
84 repeatable_set(frame);
85
86 /*
87 * Push the child. and run it.
88 */
89 if (unlang_interpret_push(&redundant->result, request, redundant->child,
92 }
93
95}
96
98{
101 unlang_load_balance_t *gext = NULL;
102 unlang_t *child;
103
104 uint32_t count = 0;
105
106#ifdef STATIC_ANALYZER
107 if (!g || !g->num_children) return UNLANG_ACTION_STOP_PROCESSING;
108#else
109 fr_assert(g != NULL);
111#endif
112
114
115 redundant = talloc_get_type_abort(frame->state, unlang_frame_state_redundant_t);
116
117 if (gext && gext->vpt) {
118 uint32_t hash, start;
119 ssize_t slen;
120 char buffer[1024];
121
122 /*
123 * Integer data types let the admin
124 * select which frame is being used.
125 */
126 if (tmpl_is_attr(gext->vpt)) {
127 fr_pair_t *vp;
128
129 slen = tmpl_find_vp(&vp, request, gext->vpt);
130 if (slen < 0) {
131 REDEBUG("Failed finding attribute %s", gext->vpt->name);
132 goto randomly_choose;
133 }
134
135 fr_assert(fr_type_is_leaf(vp->vp_type));
136
137 start = fr_value_box_hash(&vp->data) % g->num_children;
138
139 } else {
140 uint8_t *octets = NULL;
141
142 /*
143 * If the input is an IP address, prefix, etc., we don't need to convert it to a
144 * string. We can just hash the raw data directly.
145 */
146 slen = tmpl_expand(&octets, buffer, sizeof(buffer), request, gext->vpt);
147 if (slen <= 0) goto randomly_choose;
148
149 hash = fr_hash(octets, slen);
150
151 start = hash % g->num_children;
152 }
153
154 RDEBUG3("load-balance starting at child %d", (int) start);
155
156 count = 0;
157 for (child = redundant->start = g->children; child != NULL; child = child->next) {
158 count++;
159 if (count == start) {
160 redundant->start = child;
161 break;
162 }
163 }
164
165 } else {
166 randomly_choose:
167 count = 0;
168
169 /*
170 * Choose a child at random.
171 *
172 * @todo - leverage the "power of 2", as per
173 * lib/io/network.c. This is good enough for
174 * most purposes. However, in order to do this,
175 * we need to track active callers across
176 * *either* multiple modules in one thread, *or*
177 * across multiple threads.
178 *
179 * We don't have thread-specific instance data
180 * for this load-balance section. So for now,
181 * just pick a random child.
182 */
183 for (child = redundant->start = g->children; child != NULL; child = child->next) {
184 count++;
185 if ((count * (fr_rand() & 0xffffff)) < (uint32_t) 0x1000000) {
186 redundant->start = child;
187 }
188 }
189 }
190
191 fr_assert(redundant->start != NULL);
192
193 /*
194 * Plain "load-balance". Just do one child, and return the result directly bacl to the caller.
195 */
197 if (unlang_interpret_push(p_result, request, redundant->start,
200 }
202 }
203
205 return unlang_load_balance_next(p_result, request, frame);
206}
207
208
211{
212 char const *name2;
213 unlang_t *c;
216
217 tmpl_rules_t t_rules;
218
219 if (!cf_item_next(cs, NULL)) return UNLANG_IGNORE;
220
221 /*
222 * We allow unknown attributes here.
223 */
224 t_rules = *(unlang_ctx->rules);
225 t_rules.attr.allow_unknown = true;
226 RULES_VERIFY(&t_rules);
227
228 if (!unlang_compile_limit_subsection(cs, cf_section_name1(cs))) return NULL;
229
231 if (!c) return NULL;
232
234
235 /*
236 * Allow for keyed load-balance / redundant-load-balance sections.
237 */
238 name2 = cf_section_name2(cs);
239
240 /*
241 * Inside of the "modules" section, it's a virtual
242 * module. The name is a module name, not a key.
243 */
244 if (name2) {
245 if (strcmp(cf_section_name1(cf_item_to_section(cf_parent(cs))), "modules") == 0) name2 = NULL;
246 }
247
248 if (name2) {
249 fr_token_t quote;
250 ssize_t slen;
251
252 /*
253 * Create the template. All attributes and xlats are
254 * defined by now.
255 */
256 quote = cf_section_name2_quote(cs);
258 slen = tmpl_afrom_substr(gext, &gext->vpt,
259 &FR_SBUFF_IN(name2, strlen(name2)),
260 quote,
261 NULL,
262 &t_rules);
263 if (!gext->vpt) {
264 cf_canonicalize_error(cs, slen, "Failed parsing argument", name2);
265 talloc_free(g);
266 return NULL;
267 }
268
269 fr_assert(gext->vpt != NULL);
270
271 /*
272 * Fixup the templates
273 */
274 if (!pass2_fixup_tmpl(g, &gext->vpt, cf_section_to_item(cs), unlang_ctx->rules->attr.dict_def)) {
275 talloc_free(g);
276 return NULL;
277 }
278
279 switch (gext->vpt->type) {
280 default:
281 cf_log_err(cs, "Invalid type in '%s': data will not result in a load-balance key", name2);
282 talloc_free(g);
283 return NULL;
284
285 /*
286 * Allow only these ones.
287 */
288 case TMPL_TYPE_ATTR:
289 if (!fr_type_is_leaf(tmpl_attr_tail_da(gext->vpt)->type)) {
290 cf_log_err(cs, "Invalid attribute reference in '%s': load-balancing can only be done on 'leaf' data types", name2);
291 talloc_free(g);
292 return NULL;
293 }
294 break;
295
296 case TMPL_TYPE_XLAT:
297 case TMPL_TYPE_EXEC:
298 break;
299 }
300 }
301
302 return c;
303}
304
309
311{
312 int i;
314 unlang_t *child;
315 unlang_mod_actions_t actions;
316
317 /*
318 * Children of "redundant" and "redundant-load-balance"
319 * have RETURN for all actions except fail and timeout.
320 */
321 for (i = 0; i < RLM_MODULE_NUMCODES; i++) {
322 switch (i) {
323 case RLM_MODULE_FAIL:
325 actions.actions[i] = MOD_PRIORITY_MIN;
326 break;
327
328 default:
329 actions.actions[i] = MOD_ACTION_RETURN;
330 break;
331 }
332 }
333
335
336 for (child = g->children; child != NULL; child = child->next) {
337 child->actions = actions;
338 }
339}
340
341
352
353
355{
357 .name = "load-balance",
360
362 .interpret = unlang_load_balance,
363
364 .unlang_size = sizeof(unlang_load_balance_t),
365 .unlang_name = "unlang_load_balance_t",
366
367 .frame_state_size = sizeof(unlang_frame_state_redundant_t),
368 .frame_state_type = "unlang_frame_state_redundant_t",
369 });
370
372 .name = "redundant-load-balance",
375
378
379 .unlang_size = sizeof(unlang_load_balance_t),
380 .unlang_name = "unlang_load_balance_t",
381
382 .frame_state_size = sizeof(unlang_frame_state_redundant_t),
383 .frame_state_type = "unlang_frame_state_redundant_t",
384 });
385}
unlang_action_t
Returned by unlang_op_t calls, determine the next action of the interpreter.
Definition action.h:35
@ UNLANG_ACTION_PUSHED_CHILD
unlang_t pushed a new child onto the stack, execute it instead of continuing.
Definition action.h:39
@ UNLANG_ACTION_STOP_PROCESSING
Break out of processing the current request (unwind).
Definition action.h:42
@ UNLANG_ACTION_CALCULATE_RESULT
Calculate a new section rlm_rcode_t value.
Definition action.h:37
static int const char char buffer[256]
Definition acutest.h:576
#define RULES_VERIFY(_cs, _rules)
Definition cf_file.c:177
Common header for all CONF_* types.
Definition cf_priv.h:49
A section grouping multiple CONF_PAIR.
Definition cf_priv.h:101
char const * cf_section_name2(CONF_SECTION const *cs)
Return the second identifier of a CONF_SECTION.
Definition cf_util.c:1184
CONF_ITEM * cf_section_to_item(CONF_SECTION const *cs)
Cast a CONF_SECTION to a CONF_ITEM.
Definition cf_util.c:737
char const * cf_section_name1(CONF_SECTION const *cs)
Return the second identifier of a CONF_SECTION.
Definition cf_util.c:1170
CONF_SECTION * cf_item_to_section(CONF_ITEM const *ci)
Cast a CONF_ITEM to a CONF_SECTION.
Definition cf_util.c:683
fr_token_t cf_section_name2_quote(CONF_SECTION const *cs)
Return the quoting of the name2 identifier.
Definition cf_util.c:1229
#define cf_log_err(_cf, _fmt,...)
Definition cf_util.h:289
#define cf_parent(_cf)
Definition cf_util.h:101
#define cf_canonicalize_error(_ci, _slen, _msg, _str)
Definition cf_util.h:367
#define cf_item_next(_parent, _curr)
Definition cf_util.h:92
unlang_t * unlang_compile_section(unlang_t *parent, unlang_compile_ctx_t *unlang_ctx, CONF_SECTION *cs, unlang_type_t type)
Definition compile.c:1547
bool pass2_fixup_tmpl(UNUSED TALLOC_CTX *ctx, tmpl_t **vpt_p, CONF_ITEM const *ci, fr_dict_t const *dict)
Definition compile.c:104
bool unlang_compile_limit_subsection(CONF_SECTION *cs, char const *name)
Definition compile.c:1627
uint32_t fr_hash(void const *data, size_t size)
Definition hash.c:812
int unlang_interpret_push(unlang_result_t *result_p, request_t *request, unlang_t const *instruction, unlang_frame_conf_t const *conf, bool do_next_sibling)
Push a new frame onto the stack.
Definition interpret.c:283
unlang_mod_action_t priority
The priority or action for that rcode.
Definition interpret.h:136
#define UNLANG_RESULT_NOT_SET
Definition interpret.h:139
#define FRAME_CONF(_default_rcode, _top_frame)
Definition interpret.h:155
#define UNLANG_SUB_FRAME
Definition interpret.h:37
rlm_rcode_t rcode
The current rcode, from executing the instruction or merging the result from a frame.
Definition interpret.h:134
#define RDEBUG3(fmt,...)
Definition log.h:343
static TALLOC_CTX * unlang_ctx
Definition base.c:71
void unlang_register(unlang_op_t *op)
Register an operation with the interpreter.
Definition base.c:56
talloc_free(reap)
static unlang_action_t unlang_load_balance_next(unlang_result_t *p_result, request_t *request, unlang_stack_frame_t *frame)
static unlang_t * compile_load_balance_subsection(unlang_t *parent, unlang_compile_ctx_t *unlang_ctx, CONF_SECTION *cs, unlang_type_t type)
void unlang_load_balance_init(void)
static unlang_t * unlang_compile_redundant_load_balance(unlang_t *parent, unlang_compile_ctx_t *unlang_ctx, CONF_ITEM const *ci)
static void compile_redundant_actions(unlang_t *c)
static unlang_t * unlang_compile_load_balance(unlang_t *parent, unlang_compile_ctx_t *unlang_ctx, CONF_ITEM const *ci)
static unlang_action_t unlang_load_balance(unlang_result_t *p_result, request_t *request, unlang_stack_frame_t *frame)
#define unlang_redundant_load_balance
static unlang_load_balance_t * unlang_group_to_load_balance(unlang_group_t *g)
Cast a group structure to the load_balance keyword extension.
unlang_result_t result
for intermediate child results
unlang_t * child
the current child we're processing
unlang_t * start
the starting child
State of a redundant operation.
unsigned int uint32_t
long int ssize_t
unsigned char uint8_t
@ MOD_ACTION_RETURN
stop processing the section, and return the rcode with unset priority
Definition mod_action.h:40
#define MOD_PRIORITY_MIN
Definition mod_action.h:59
unlang_mod_action_t actions[RLM_MODULE_NUMCODES]
Definition mod_action.h:62
#define fr_assert(_expr)
Definition rad_assert.h:38
#define REDEBUG(fmt,...)
Definition radclient.h:52
uint32_t fr_rand(void)
Return a 32-bit random number.
Definition rand.c:105
@ RLM_MODULE_FAIL
Module failed, don't reply.
Definition rcode.h:42
@ RLM_MODULE_TIMEOUT
Module (or section) timed out.
Definition rcode.h:50
@ RLM_MODULE_NOT_SET
Error resolving rcode (should not be returned by modules).
Definition rcode.h:52
@ RLM_MODULE_NUMCODES
How many valid return codes there are.
Definition rcode.h:51
static unsigned int hash(char const *username, unsigned int tablesize)
Definition rlm_passwd.c:132
#define FR_SBUFF_IN(_start, _len_or_end)
int tmpl_find_vp(fr_pair_t **out, request_t *request, tmpl_t const *vpt))
Returns the first VP matching a tmpl_t.
Definition tmpl_eval.c:771
#define tmpl_is_attr(vpt)
Definition tmpl.h:208
@ TMPL_TYPE_ATTR
Reference to one or more attributes.
Definition tmpl.h:142
@ TMPL_TYPE_XLAT
Pre-parsed xlat expansion.
Definition tmpl.h:146
@ TMPL_TYPE_EXEC
Callout to an external script or program.
Definition tmpl.h:150
ssize_t tmpl_afrom_substr(TALLOC_CTX *ctx, tmpl_t **out, fr_sbuff_t *in, fr_token_t quote, fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules))
Convert an arbitrary string into a tmpl_t.
#define tmpl_expand(_out, _buff, _buff_len, _request, _vpt)
Expand a tmpl to a C type, using existing storage to hold variably sized types.
Definition tmpl.h:1052
tmpl_attr_rules_t attr
Rules/data for parsing attribute references.
Definition tmpl.h:335
static fr_dict_attr_t const * tmpl_attr_tail_da(tmpl_t const *vpt)
Return the last attribute reference da.
Definition tmpl.h:801
Optional arguments passed to vp_tmpl functions.
Definition tmpl.h:332
return count
Definition module.c:155
fr_aka_sim_id_type_t type
fr_pair_t * vp
uint8_t allow_unknown
Allow unknown attributes i.e.
Definition tmpl.h:301
Stores an attribute, a value and various bits of other data.
Definition pair.h:68
enum fr_token fr_token_t
Private interpreter structures and functions.
unlang_t * next
Next node (executed on UNLANG_ACTION_EXECUTE_NEXT et al).
void * state
Stack frame specialisations.
unlang_mod_actions_t actions
Priorities, etc. for the various return codes.
#define UNLANG_NEXT_STOP
Definition unlang_priv.h:97
#define UNLANG_IGNORE
static unlang_group_t * unlang_generic_to_group(unlang_t const *p)
unlang_type_t
Types of unlang_t nodes.
Definition unlang_priv.h:45
@ UNLANG_TYPE_LOAD_BALANCE
Load balance section.
Definition unlang_priv.h:51
@ UNLANG_TYPE_REDUNDANT_LOAD_BALANCE
Redundant load balance section.
Definition unlang_priv.h:52
unlang_t const * instruction
The unlang node we're evaluating.
@ UNLANG_OP_FLAG_DEBUG_BRACES
Print debug braces.
@ UNLANG_OP_FLAG_RCODE_SET
Set request->rcode to the result of this operation.
static void repeatable_set(unlang_stack_frame_t *frame)
unlang_process_t process
function to call for interpreting this stack frame
unlang_type_t type
The specialisation of this node.
unlang_t * children
Children beneath this group.
Generic representation of a grouping.
An unlang operation.
A node in a graph of unlang_op_t (s) that we execute.
Our interpreter stack, as distinct from the C stack.
static fr_slen_t parent
Definition pair.h:839
#define fr_type_is_leaf(_x)
Definition types.h:389
uint32_t fr_value_box_hash(fr_value_box_t const *vb)
Hash the contents of a value box.
Definition value.c:6314