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: 5ad345fcc443d095a88cafb4293fce4d5d5f0e01 $
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) {
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
114 if (!g->num_children) {
115 *p_result = RLM_MODULE_NOOP;
117 }
118
120
121 RDEBUG4("%s setting up", frame->instruction->debug_name);
122
123 redundant = talloc_get_type_abort(frame->state,
125
126 if (gext && gext->vpt) {
127 uint32_t hash, start;
128 ssize_t slen;
129 char buffer[1024];
130
131 /*
132 * Integer data types let the admin
133 * select which frame is being used.
134 */
135 if (tmpl_is_attr(gext->vpt) &&
136 ((tmpl_attr_tail_da(gext->vpt)->type == FR_TYPE_UINT8) ||
137 (tmpl_attr_tail_da(gext->vpt)->type == FR_TYPE_UINT16) ||
138 (tmpl_attr_tail_da(gext->vpt)->type == FR_TYPE_UINT32) ||
139 (tmpl_attr_tail_da(gext->vpt)->type == FR_TYPE_UINT64))) {
140 fr_pair_t *vp;
141
142 slen = tmpl_find_vp(&vp, request, gext->vpt);
143 if (slen < 0) {
144 REDEBUG("Failed finding attribute %s", gext->vpt->name);
145 goto randomly_choose;
146 }
147
148 switch (tmpl_attr_tail_da(gext->vpt)->type) {
149 case FR_TYPE_UINT8:
150 start = ((uint32_t) vp->vp_uint8) % g->num_children;
151 break;
152
153 case FR_TYPE_UINT16:
154 start = ((uint32_t) vp->vp_uint16) % g->num_children;
155 break;
156
157 case FR_TYPE_UINT32:
158 start = vp->vp_uint32 % g->num_children;
159 break;
160
161 case FR_TYPE_UINT64:
162 start = (uint32_t) (vp->vp_uint64 % ((uint64_t) g->num_children));
163 break;
164
165 default:
166 goto randomly_choose;
167 }
168
169 } else {
170 uint8_t *octets = NULL;
171
172 /*
173 * If the input is an IP address, prefix, etc., we don't need to convert it to a
174 * string. We can just hash the raw data directly.
175 */
176 slen = tmpl_expand(&octets, buffer, sizeof(buffer), request, gext->vpt);
177 if (slen <= 0) goto randomly_choose;
178
179 hash = fr_hash(octets, slen);
180
181 start = hash % g->num_children;
182 }
183
184 RDEBUG3("load-balance starting at child %d", (int) start);
185
186 count = 0;
187 for (redundant->child = redundant->found = g->children;
188 redundant->child != NULL;
189 redundant->child = redundant->child->next) {
190 count++;
191 if (count == start) {
192 redundant->found = redundant->child;
193 break;
194 }
195 }
196
197 } else {
198 randomly_choose:
199 count = 0;
200
201 /*
202 * Choose a child at random.
203 *
204 * @todo - leverage the "power of 2", as per
205 * lib/io/network.c. This is good enough for
206 * most purposes. However, in order to do this,
207 * we need to track active callers across
208 * *either* multiple modules in one thread, *or*
209 * across multiple threads.
210 *
211 * We don't have thread-specific instance data
212 * for this load-balance section. So for now,
213 * just pick a random child.
214 */
215 for (redundant->child = redundant->found = g->children;
216 redundant->child != NULL;
217 redundant->child = redundant->child->next) {
218 count++;
219
220 if ((count * (fr_rand() & 0xffffff)) < (uint32_t) 0x1000000) {
221 redundant->found = redundant->child;
222 }
223 }
224 }
225
226 /*
227 * Plain "load-balance". Just do one child.
228 */
230 if (unlang_interpret_push(request, redundant->found,
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,
255 .frame_state_size = sizeof(unlang_frame_state_redundant_t),
256 .frame_state_type = "unlang_frame_state_redundant_t",
257 });
258
260 &(unlang_op_t){
261 .name = "redundant-load-balance group",
264 .frame_state_size = sizeof(unlang_frame_state_redundant_t),
265 .frame_state_type = "unlang_frame_state_redundant_t",
266 });
267}
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(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:143
#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
unsigned char uint8_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: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.
rlm_rcode_t result
The result from executing the instruction.
@ 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.