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: a5691c3def3d928f48be3c7d9031ce3ff9beeb7e $
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 "load_balance_priv.h"
30#include "module_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#ifdef STATIC_ANALYZER
41 if (!redundant->found) RETURN_UNLANG_FAIL;
42#endif
43 /*
44 * Set up the first round versus subsequent ones.
45 */
46 if (!redundant->child) {
47 redundant->child = redundant->found;
48
49 } else {
50 /*
51 * child is NULL on the first pass. But if it's
52 * back to the found one, then we're done.
53 */
54 if (redundant->child == redundant->found) {
55 /* DON'T change p_result, as it is taken from the child */
57 }
58
59 RDEBUG4("%s resuming", frame->instruction->debug_name);
60
61 /*
62 * We are in a resumed frame. The module we
63 * chose failed, so we have to go through the
64 * process again.
65 */
66
67 fr_assert(frame->instruction->type != UNLANG_TYPE_LOAD_BALANCE); /* this is never called again */
68
69 /*
70 * If the current child says "return", then do
71 * so.
72 */
73 if ((p_result->rcode != RLM_MODULE_NOT_SET) &&
74 (redundant->child->actions.actions[p_result->rcode] == MOD_ACTION_RETURN)) {
75 /* DON'T change p_result, as it is taken from the child */
77 }
78 }
79
80 /*
81 * Push the child, and yield for a later return.
82 */
83 if (unlang_interpret_push(NULL, request, redundant->child,
86 }
87
88 /*
89 * Now that we've pushed this child, make the next call
90 * use the next child, wrapping around to the beginning.
91 *
92 * @todo - track the one we chose, and if it fails, do
93 * the load-balancing again, except this time skipping
94 * the failed module. AND, keep track of multiple failed
95 * modules in the unlang_frame_state_redundant_t
96 * structure.
97 */
98 redundant->child = redundant->child->next;
99 if (!redundant->child) redundant->child = g->children;
100
101 repeatable_set(frame);
102
104}
105
107{
110 unlang_load_balance_t *gext = NULL;
111
112 uint32_t count = 0;
113
115
117
118 RDEBUG4("%s setting up", frame->instruction->debug_name);
119
120 redundant = talloc_get_type_abort(frame->state,
122
123 if (gext && gext->vpt) {
124 uint32_t hash, start;
125 ssize_t slen;
126 char buffer[1024];
127
128 /*
129 * Integer data types let the admin
130 * select which frame is being used.
131 */
132 if (tmpl_is_attr(gext->vpt) &&
133 ((tmpl_attr_tail_da(gext->vpt)->type == FR_TYPE_UINT8) ||
134 (tmpl_attr_tail_da(gext->vpt)->type == FR_TYPE_UINT16) ||
135 (tmpl_attr_tail_da(gext->vpt)->type == FR_TYPE_UINT32) ||
136 (tmpl_attr_tail_da(gext->vpt)->type == FR_TYPE_UINT64))) {
137 fr_pair_t *vp;
138
139 slen = tmpl_find_vp(&vp, request, gext->vpt);
140 if (slen < 0) {
141 REDEBUG("Failed finding attribute %s", gext->vpt->name);
142 goto randomly_choose;
143 }
144
145 switch (tmpl_attr_tail_da(gext->vpt)->type) {
146 case FR_TYPE_UINT8:
147 start = ((uint32_t) vp->vp_uint8) % g->num_children;
148 break;
149
150 case FR_TYPE_UINT16:
151 start = ((uint32_t) vp->vp_uint16) % g->num_children;
152 break;
153
154 case FR_TYPE_UINT32:
155 start = vp->vp_uint32 % g->num_children;
156 break;
157
158 case FR_TYPE_UINT64:
159 start = (uint32_t) (vp->vp_uint64 % ((uint64_t) g->num_children));
160 break;
161
162 default:
163 goto randomly_choose;
164 }
165
166 } else {
167 uint8_t *octets = NULL;
168
169 /*
170 * If the input is an IP address, prefix, etc., we don't need to convert it to a
171 * string. We can just hash the raw data directly.
172 */
173 slen = tmpl_expand(&octets, buffer, sizeof(buffer), request, gext->vpt);
174 if (slen <= 0) goto randomly_choose;
175
176 hash = fr_hash(octets, slen);
177
178 start = hash % g->num_children;
179 }
180
181 RDEBUG3("load-balance starting at child %d", (int) start);
182
183 count = 0;
184 for (redundant->child = redundant->found = g->children;
185 redundant->child != NULL;
186 redundant->child = redundant->child->next) {
187 count++;
188 if (count == start) {
189 redundant->found = redundant->child;
190 break;
191 }
192 }
193
194 } else {
195 randomly_choose:
196 count = 0;
197
198 /*
199 * Choose a child at random.
200 *
201 * @todo - leverage the "power of 2", as per
202 * lib/io/network.c. This is good enough for
203 * most purposes. However, in order to do this,
204 * we need to track active callers across
205 * *either* multiple modules in one thread, *or*
206 * across multiple threads.
207 *
208 * We don't have thread-specific instance data
209 * for this load-balance section. So for now,
210 * just pick a random child.
211 */
212 for (redundant->child = redundant->found = g->children;
213 redundant->child != NULL;
214 redundant->child = redundant->child->next) {
215 count++;
216
217 if ((count * (fr_rand() & 0xffffff)) < (uint32_t) 0x1000000) {
218 redundant->found = redundant->child;
219 }
220 }
221 }
222
223 /*
224 * Plain "load-balance". Just do one child.
225 */
227 if (unlang_interpret_push(NULL, request, redundant->found,
230 }
232 }
233
234 /*
235 * "redundant" and "redundant-load-balance" starts at
236 * "found", but we need to indicate that we're at the
237 * first child.
238 */
239 redundant->child = NULL;
240
242 return unlang_load_balance_next(p_result, request, frame);
243}
244
246{
248 &(unlang_op_t){
249 .name = "load-balance group",
250 .interpret = unlang_load_balance,
252 .frame_state_size = sizeof(unlang_frame_state_redundant_t),
253 .frame_state_type = "unlang_frame_state_redundant_t",
254 });
255
257 &(unlang_op_t){
258 .name = "redundant-load-balance group",
261 .frame_state_size = sizeof(unlang_frame_state_redundant_t),
262 .frame_state_type = "unlang_frame_state_redundant_t",
263 });
264}
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
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
#define FRAME_CONF(_default_rcode, _top_frame)
Definition interpret.h:153
#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
#define RDEBUG4(fmt,...)
Definition log.h:344
void unlang_register(int type, unlang_op_t *op)
Register an operation with the interpreter.
Definition base.c:63
static unlang_action_t unlang_load_balance_next(unlang_result_t *p_result, request_t *request, unlang_stack_frame_t *frame)
void unlang_load_balance_init(void)
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.
State of a redundant operation.
@ FR_TYPE_UINT16
16 Bit unsigned integer.
@ FR_TYPE_UINT8
8 Bit unsigned integer.
@ FR_TYPE_UINT32
32 Bit unsigned integer.
@ FR_TYPE_UINT64
64 Bit unsigned integer.
unsigned int uint32_t
long int ssize_t
unsigned char uint8_t
@ MOD_ACTION_RETURN
Definition mod_action.h:43
unlang_mod_action_t actions[RLM_MODULE_NUMCODES]
Definition mod_action.h:64
Declarations for the unlang module interface.
#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
#define RETURN_UNLANG_FAIL
Definition rcode.h:57
@ RLM_MODULE_NOT_SET
Error resolving rcode (should not be returned by modules).
Definition rcode.h:52
#define RETURN_UNLANG_NOOP
Definition rcode.h:63
static unsigned int hash(char const *username, unsigned int tablesize)
Definition rlm_passwd.c:132
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:768
#define tmpl_is_attr(vpt)
Definition tmpl.h:208
#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
static fr_dict_attr_t const * tmpl_attr_tail_da(tmpl_t const *vpt)
Return the last attribute reference da.
Definition tmpl.h:801
return count
Definition module.c:155
fr_pair_t * vp
Stores an attribute, a value and various bits of other data.
Definition pair.h:68
unlang_t * next
Next node (executed on UNLANG_ACTION_EXECUTE_NEXT et al).
char const * debug_name
Printed in log messages when the node is executed.
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
static unlang_group_t * unlang_generic_to_group(unlang_t const *p)
@ 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.
Our interpreter stack, as distinct from the C stack.