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: 37742b3bcc9d7e509030a2241d082a18ee2a2f58 $
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/util/hash.h>
26#include <freeradius-devel/util/rand.h>
27
28#include "load_balance_priv.h"
29#include "module_priv.h"
30
31#define unlang_redundant_load_balance unlang_load_balance
32
35{
36 unlang_frame_state_redundant_t *redundant = talloc_get_type_abort(frame->state, unlang_frame_state_redundant_t);
38
39#ifdef STATIC_ANALYZER
40 if (!redundant->found) {
41 *p_result = RLM_MODULE_FAIL;
43 }
44#endif
45 /*
46 * Set up the first round versus subsequent ones.
47 */
48 if (!redundant->child) {
49 redundant->child = redundant->found;
50
51 } else {
52 /*
53 * child is NULL on the first pass. But if it's
54 * back to the found one, then we're done.
55 */
56 if (redundant->child == redundant->found) {
57 /* DON'T change p_result, as it is taken from the child */
59 }
60
61 RDEBUG4("%s resuming", frame->instruction->debug_name);
62
63 /*
64 * We are in a resumed frame. The module we
65 * chose failed, so we have to go through the
66 * process again.
67 */
68
69 fr_assert(frame->instruction->type != UNLANG_TYPE_LOAD_BALANCE); /* this is never called again */
70
71 /*
72 * If the current child says "return", then do
73 * so.
74 */
75 if (redundant->child->actions.actions[*p_result] == MOD_ACTION_RETURN) {
76 /* DON'T change p_result, as it is taken from the child */
78 }
79 }
80
81 /*
82 * Push the child, and yield for a later return.
83 */
84 if (unlang_interpret_push(request, redundant->child, frame->result, UNLANG_NEXT_STOP, UNLANG_SUB_FRAME) < 0) {
85 *p_result = RLM_MODULE_FAIL;
87 }
88
89 /*
90 * Now that we've pushed this child, make the next call
91 * use the next child, wrapping around to the beginning.
92 *
93 * @todo - track the one we chose, and if it fails, do
94 * the load-balancing again, except this time skipping
95 * the failed module. AND, keep track of multiple failed
96 * modules in the unlang_frame_state_redundant_t
97 * structure.
98 */
99 redundant->child = redundant->child->next;
100 if (!redundant->child) redundant->child = g->children;
101
102 repeatable_set(frame);
103
105}
106
108{
111 unlang_load_balance_t *gext = NULL;
112
113 uint32_t count = 0;
114
115 if (!g->num_children) {
116 *p_result = RLM_MODULE_NOOP;
118 }
119
121
122 RDEBUG4("%s setting up", frame->instruction->debug_name);
123
124 redundant = talloc_get_type_abort(frame->state,
126
127 if (gext && gext->vpt) {
128 uint32_t hash, start;
129 ssize_t slen;
130 char const *p = NULL;
131 char buffer[1024];
132
133 /*
134 * Integer data types let the admin
135 * select which frame is being used.
136 */
137 if (tmpl_is_attr(gext->vpt) &&
138 ((tmpl_attr_tail_da(gext->vpt)->type == FR_TYPE_UINT8) ||
139 (tmpl_attr_tail_da(gext->vpt)->type == FR_TYPE_UINT16) ||
140 (tmpl_attr_tail_da(gext->vpt)->type == FR_TYPE_UINT32) ||
141 (tmpl_attr_tail_da(gext->vpt)->type == FR_TYPE_UINT64))) {
142 fr_pair_t *vp;
143
144 slen = tmpl_find_vp(&vp, request, gext->vpt);
145 if (slen < 0) {
146 REDEBUG("Failed finding attribute %s", gext->vpt->name);
147 goto randomly_choose;
148 }
149
150 switch (tmpl_attr_tail_da(gext->vpt)->type) {
151 case FR_TYPE_UINT8:
152 start = ((uint32_t) vp->vp_uint8) % g->num_children;
153 break;
154
155 case FR_TYPE_UINT16:
156 start = ((uint32_t) vp->vp_uint16) % g->num_children;
157 break;
158
159 case FR_TYPE_UINT32:
160 start = vp->vp_uint32 % g->num_children;
161 break;
162
163 case FR_TYPE_UINT64:
164 start = (uint32_t) (vp->vp_uint64 % ((uint64_t) g->num_children));
165 break;
166
167 default:
168 goto randomly_choose;
169 }
170
171 } else {
172 slen = tmpl_expand(&p, buffer, sizeof(buffer), request, gext->vpt, NULL, NULL);
173 if (slen < 0) {
174 REDEBUG("Failed expanding template");
175 goto randomly_choose;
176 }
177
178 hash = fr_hash(p, slen);
179
180 start = hash % g->num_children;
181 }
182
183 RDEBUG3("load-balance starting at child %d", (int) start);
184
185 count = 0;
186 for (redundant->child = redundant->found = g->children;
187 redundant->child != NULL;
188 redundant->child = redundant->child->next) {
189 count++;
190 if (count == start) {
191 redundant->found = redundant->child;
192 break;
193 }
194 }
195
196 } else {
197 randomly_choose:
198 count = 0;
199
200 /*
201 * Choose a child at random.
202 *
203 * @todo - leverage the "power of 2", as per
204 * lib/io/network.c. This is good enough for
205 * most purposes. However, in order to do this,
206 * we need to track active callers across
207 * *either* multiple modules in one thread, *or*
208 * across multiple threads.
209 *
210 * We don't have thread-specific instance data
211 * for this load-balance section. So for now,
212 * just pick a random child.
213 */
214 for (redundant->child = redundant->found = g->children;
215 redundant->child != NULL;
216 redundant->child = redundant->child->next) {
217 count++;
218
219 if ((count * (fr_rand() & 0xffffff)) < (uint32_t) 0x1000000) {
220 redundant->found = redundant->child;
221 }
222 }
223 }
224
225 /*
226 * Plain "load-balance". Just do one child.
227 */
229 if (unlang_interpret_push(request, redundant->found,
231 *p_result = RLM_MODULE_FAIL;
233 }
235 }
236
237 /*
238 * "redundant" and "redundant-load-balance" starts at
239 * "found", but we need to indicate that we're at the
240 * first child.
241 */
242 redundant->child = NULL;
243
245 return unlang_load_balance_next(p_result, request, frame);
246}
247
249{
251 &(unlang_op_t){
252 .name = "load-balance group",
253 .interpret = unlang_load_balance,
254 .rcode_set = true,
255 .debug_braces = true,
256 .frame_state_size = sizeof(unlang_frame_state_redundant_t),
257 .frame_state_type = "unlang_frame_state_redundant_t",
258 });
259
261 &(unlang_op_t){
262 .name = "redundant-load-balance group",
264 .rcode_set = true,
265 .debug_braces = true,
266 .frame_state_size = sizeof(unlang_frame_state_redundant_t),
267 .frame_state_type = "unlang_frame_state_redundant_t",
268 });
269}
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:43
@ 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(request_t *request, unlang_t const *instruction, rlm_rcode_t default_rcode, bool do_next_sibling, bool top_frame)
Push a new frame onto the stack.
Definition interpret.c:161
#define UNLANG_SUB_FRAME
Definition interpret.h:36
#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(rlm_rcode_t *p_result, request_t *request, unlang_stack_frame_t *frame)
static unlang_action_t unlang_load_balance(rlm_rcode_t *p_result, request_t *request, unlang_stack_frame_t *frame)
void unlang_load_balance_init(void)
#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
@ MOD_ACTION_RETURN
Definition mod_action.h:40
unlang_mod_action_t actions[RLM_MODULE_NUMCODES]
Definition mod_action.h:62
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
rlm_rcode_t
Return codes indicating the result of the module call.
Definition rcode.h:40
@ RLM_MODULE_FAIL
Module failed, don't reply.
Definition rcode.h:42
@ RLM_MODULE_NOOP
Module succeeded without doing anything.
Definition rcode.h:48
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:889
#define tmpl_is_attr(vpt)
Definition tmpl.h:213
static fr_dict_attr_t const * tmpl_attr_tail_da(tmpl_t const *vpt)
Return the last attribute reference da.
Definition tmpl.h:812
#define tmpl_expand(_out, _buff, _buff_len, _request, _vpt, _escape, _escape_ctx)
Expand a tmpl to a C type, using existing storage to hold variably sized types.
Definition tmpl.h:1060
return count
Definition module.c:163
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:92
static unlang_group_t * unlang_generic_to_group(unlang_t const *p)
@ UNLANG_TYPE_LOAD_BALANCE
Load balance section.
Definition unlang_priv.h:50
@ UNLANG_TYPE_REDUNDANT_LOAD_BALANCE
Redundant load balance section.
Definition unlang_priv.h:51
unlang_t const * instruction
The unlang node we're evaluating.
rlm_rcode_t result
The result from executing the instruction.
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.