The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
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 
34  unlang_stack_frame_t *frame)
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  */
228  if (frame->instruction->type == UNLANG_TYPE_LOAD_BALANCE) {
229  if (unlang_interpret_push(request, redundant->found,
230  frame->result, UNLANG_NEXT_STOP, UNLANG_SUB_FRAME) < 0) {
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",
263  .interpret = unlang_redundant_load_balance,
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:574
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)
Definition: load_balance.c:33
static unlang_action_t unlang_load_balance(rlm_rcode_t *p_result, request_t *request, unlang_stack_frame_t *frame)
Definition: load_balance.c:107
void unlang_load_balance_init(void)
Definition: load_balance.c:248
#define unlang_redundant_load_balance
Definition: load_balance.c:31
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.
Definition: merged_model.c:98
@ FR_TYPE_UINT8
8 Bit unsigned integer.
Definition: merged_model.c:97
@ FR_TYPE_UINT32
32 Bit unsigned integer.
Definition: merged_model.c:99
@ FR_TYPE_UINT64
64 Bit unsigned integer.
Definition: merged_model.c:100
unsigned int uint32_t
Definition: merged_model.c:33
long int ssize_t
Definition: merged_model.c:24
@ 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 REDEBUG(fmt,...)
Definition: radclient.h:52
uint32_t fr_rand(void)
Return a 32-bit random number.
Definition: rand.c:106
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
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_is_attr(vpt)
Definition: tmpl.h:213
#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_assert(0)
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).
Definition: unlang_priv.h:114
char const * debug_name
Printed in log messages when the node is executed.
Definition: unlang_priv.h:116
void * state
Stack frame specialisations.
Definition: unlang_priv.h:296
unlang_mod_actions_t actions
Priorities, etc. for the various return codes.
Definition: unlang_priv.h:121
#define UNLANG_NEXT_STOP
Definition: unlang_priv.h:92
static unlang_group_t * unlang_generic_to_group(unlang_t const *p)
Definition: unlang_priv.h:531
@ 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.
Definition: unlang_priv.h:281
rlm_rcode_t result
The result from executing the instruction.
Definition: unlang_priv.h:300
static void repeatable_set(unlang_stack_frame_t *frame)
Definition: unlang_priv.h:345
unlang_process_t process
function to call for interpreting this stack frame
Definition: unlang_priv.h:284
unlang_type_t type
The specialisation of this node.
Definition: unlang_priv.h:117
unlang_t * children
Children beneath this group.
Definition: unlang_priv.h:147
Generic representation of a grouping.
Definition: unlang_priv.h:145
An unlang operation.
Definition: unlang_priv.h:204
Our interpreter stack, as distinct from the C stack.
Definition: unlang_priv.h:280