The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
process.h
Go to the documentation of this file.
1 #pragma once
2 /*
3  * This program is free software; you can redistribute it and/or modify
4  * it under the terms of the GNU General Public License as published by
5  * the Free Software Foundation; either version 2 of the License, or
6  * (at your option) any later version.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program; if not, write to the Free Software
15  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
16  */
17 
18 /**
19  * $Id: 5dab45a8ae1ddbca3463eba8a9f968b915260d46 $
20  *
21  * @file src/lib/server/process.h
22  * @brief Declarations for functions which process packet state machines
23  *
24  * This is a convenience header to simplify defining packet processing state machines.
25  *
26  * The following macros must be defined before this header is included:
27  *
28  * - PROCESS_INST the type of structure that holds instance data for the process module.
29  * - PROCESS_PACKET_TYPE an enum, or generic type (uint32) that can hold
30  * all valid packet types.
31  * - PROCESS_PACKET_CODE_VALID the name of a macro or function which accepts one argument
32  * and evaluates to true if the packet code is valid.
33  *
34  * The following macros may (optionally) be defined before this header is included:
35  *
36  * - PROCESS_CODE_MAX the highest valid protocol packet code + 1.
37  * - PROCESS_CODE_DO_NOT_RESPOND The packet code that's used to indicate that no response
38  * should be sent.
39  * - PROCESS_STATE_EXTRA_FIELDS extra fields to add to the fr_process_state_t structure.
40  *
41  * @copyright 2021 The FreeRADIUS server project
42  * @copyright 2021 Network RADIUS SAS (legal@networkradius.com)
43  */
44 
45 #ifdef __cplusplus
46 extern "C" {
47 #endif
48 
49 #include <freeradius-devel/server/virtual_servers.h>
50 #include <freeradius-devel/server/pair.h>
51 
52 /** Common public symbol definition for all process modules
53  */
54 typedef struct fr_process_module_s {
55  module_t common; //!< Common fields for all loadable modules.
56 
57  module_method_t process; //!< Process packets
58  virtual_server_compile_t const *compile_list; //!< list of processing sections
59  fr_dict_t const **dict; //!< pointer to local fr_dict_t *
61 
62 /** Trace each state function as it's entered
63  */
64 #ifndef NDEBUG
65 # define PROCESS_TRACE RDEBUG3("Entered state %s", __FUNCTION__)
66 #else
67 # define PROCESS_TRACE
68 #endif
69 
70 /** Convenience macro for providing CONF_SECTION offsets in section compilation arrays
71  *
72  */
73 #ifdef PROCESS_INST
74 # define PROCESS_CONF_OFFSET(_x) offsetof(PROCESS_INST, sections._x)
75 #endif
76 
77 #if defined(PROCESS_INST) && defined(PROCESS_PACKET_TYPE) && defined(PROCESS_PACKET_CODE_VALID)
78 typedef PROCESS_PACKET_TYPE fr_process_rcode_t[RLM_MODULE_NUMCODES];
79 
80 #ifndef PROCESS_STATE_EXTRA_FIELDS
81 # define PROCESS_STATE_EXTRA_FIELDS
82 #endif
83 
84 /*
85  * Process state machine tables for rcode to packet.
86  */
87 typedef struct {
88  PROCESS_PACKET_TYPE packet_type[RLM_MODULE_NUMCODES]; //!< rcode to packet type mapping.
89  PROCESS_PACKET_TYPE default_reply; //!< if not otherwise set
90  size_t section_offset; //!< Where to look in the process instance for
91  ///< a pointer to the section we should execute.
92  rlm_rcode_t rcode; //!< Default rcode
93  module_method_t resume; //!< Function to call after running a recv section.
94 
95  /*
96  * Each state has only one "recv" or "send".
97  */
98  union {
99  module_method_t recv; //!< Method to call when receiving this type of packet.
100  module_method_t send; //!< Method to call when sending this type of packet.
101  };
103 } fr_process_state_t;
104 
105 /*
106  * Some protocols have the same packet codes for requests and replies.
107  */
108 #ifndef PROCESS_SEND_RECV
109 #define process_state_packet process_state
110 #define process_state_reply process_state
111 static fr_process_state_t const process_state[];
112 #else
113 static fr_process_state_t const process_state_packet[];
114 static fr_process_state_t const process_state_reply[];
115 #endif
116 
117 /*
118  * Process state machine functions
119  */
120 #define UPDATE_STATE_CS(_x) \
121 do { \
122  state = &process_state_ ## _x[request->_x->code]; \
123  memcpy(&cs, (CONF_SECTION * const *) (((uint8_t const *) &inst->sections) + state->section_offset), sizeof(cs)); \
124 } while (0)
125 
126 #define UPDATE_STATE(_x) state = &process_state_ ## _x [request->_x->code]
127 
128 #define RECV(_x) static inline unlang_action_t recv_ ## _x(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
129 #define SEND(_x) static inline unlang_action_t send_ ## _x(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
130 #define RESUME(_x) static inline unlang_action_t resume_ ## _x(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
131 #define RESUME_NO_MCTX(_x) static inline unlang_action_t resume_ ## _x(rlm_rcode_t *p_result, UNUSED module_ctx_t const *mctx, request_t *request)
132 
133 /** Call a module method with a new rctx
134  *
135  * @note This should be used to add a rctxs when calling the initial recv section.
136  *
137  * @param[out] p_result Pointer to the result code.
138  * @param[in] mctx Module context.
139  * @param[in] request Request.
140  * @param[in] method Method to call.
141  * @param[in] rctx Resume context to use to override the one in the mctx.
142  * @return Result of the method call.
143  */
144 static inline CC_HINT(always_inline)
145 unlang_action_t process_with_rctx(rlm_rcode_t *p_result, module_ctx_t const *mctx,
146  request_t *request, module_method_t method, void *rctx)
147 {
148  module_ctx_t our_mctx = *mctx;
149  our_mctx.rctx = rctx;
150 
151  return method(p_result, &our_mctx, request);
152 }
153 
154 /** Call a named recv function directly
155  */
156 #define CALL_RECV(_x) recv_ ## _x(p_result, mctx, request)
157 
158 /** Call a named recv function directly with a new rctx
159  */
160 #define CALL_RECV_RCTX(_x, _rctx) process_with_rctx(p_result, mctx, request, recv_ ## _x, _rctx);
161 
162 /** Call a named send function directly
163  */
164 #define CALL_SEND(_x) send_ ## _x(p_result, mctx, request)
165 
166 /** Call a named resume function directly
167  */
168 #define CALL_RESUME(_x) resume_ ## _x(p_result, mctx, request)
169 
170 /** Call the send function for the current state
171  */
172 #define CALL_SEND_STATE(_state) state->send(p_result, mctx, request)
173 
174 /** Set the current reply code, and call the send function for that state
175  */
176 #define CALL_SEND_TYPE(_x) call_send_type(process_state_reply[(request->reply->code = _x)].send, p_result, mctx, request)
177 
178 static inline unlang_action_t call_send_type(module_method_t send, \
179  rlm_rcode_t *p_result, module_ctx_t const *mctx,
180  request_t *request)
181 {
182  /*
183  * Stupid hack to stop this being honoured
184  * by send_generic.
185  */
187  return send(p_result, mctx, request);
188 }
189 
190 RECV(generic)
191 {
192  CONF_SECTION *cs;
193  fr_process_state_t const *state;
194  PROCESS_INST const *inst = mctx->mi->data;
195 
197 
198  UPDATE_STATE_CS(packet);
199 
200  if (!state->recv) {
201  char const *name;
202 
204  if (name) {
205  REDEBUG("Invalid packet type (%s)", name);
206  } else {
207  REDEBUG("Invalid packet type (%u)", request->packet->code);
208  }
210  }
211 
212 
213  if (cs) RDEBUG("Running '%s %s' from file %s", cf_section_name1(cs), cf_section_name2(cs), cf_filename(cs));
214  return unlang_module_yield_to_section(p_result, request,
215  cs, state->rcode, state->resume,
216  NULL, 0, mctx->rctx);
217 }
218 
219 RESUME(recv_generic)
220 {
221  rlm_rcode_t rcode = *p_result;
222  fr_process_state_t const *state;
223 
225 
227 
228  UPDATE_STATE(packet);
229 
230  request->reply->code = state->packet_type[rcode];
231  if (!request->reply->code) request->reply->code = state->default_reply;
232 #ifdef PROCESS_CODE_DO_NOT_RESPOND
233  if (!request->reply->code) request->reply->code = PROCESS_CODE_DO_NOT_RESPOND;
234 
235 #endif
236  fr_assert(PROCESS_PACKET_CODE_VALID(request->reply->code));
237 
238  UPDATE_STATE(reply);
239  fr_assert(state->send != NULL);
240  return state->send(p_result, mctx, request);
241 }
242 
243 RESUME_NO_MCTX(recv_no_send)
244 {
245  rlm_rcode_t rcode = *p_result;
246  fr_process_state_t const *state;
247 
249 
251 
252  UPDATE_STATE(packet);
253 
254  request->reply->code = state->packet_type[rcode];
255  if (!request->reply->code) request->reply->code = state->default_reply;
256 #ifdef PROCESS_CODE_DO_NOT_RESPOND
257  if (!request->reply->code) request->reply->code = PROCESS_CODE_DO_NOT_RESPOND;
258 
259 #endif
260  fr_assert(request->reply->code != 0);
261 
263 }
264 
265 SEND(generic)
266 {
267  fr_pair_t *vp;
268  CONF_SECTION *cs;
269  fr_process_state_t const *state;
270  PROCESS_INST const *inst = mctx->mi->data;
271 
273 
274 #ifndef NDEBUG
275  if (!(
277  (request->reply->code == PROCESS_CODE_DO_NOT_RESPOND) ||
278 # endif
279  PROCESS_PACKET_CODE_VALID(request->reply->code))) fr_assert(0);
280 #endif
281 
282  UPDATE_STATE_CS(reply);
283 
284  /*
285  * Allow for over-ride of reply code, IF it's
286  * within range, AND we've pre-compiled the
287  * unlang.
288  *
289  * Add reply->packet-type in case we're
290  * being called via the `call {}` keyword.
291  *
292  * @todo - enforce that this is an allowed reply for the
293  * request.
294  */
296  case 0: /* Does not exist */
297  update_packet_type:
298  vp->vp_uint32 = request->reply->code;
299  break;
300 
301  case 1: /* Exists */
302  if (
303 #ifdef PROCESS_CODE_MAX
304  (vp->vp_uint32 != PROCESS_CODE_MAX) &&
305 #endif
306  PROCESS_PACKET_CODE_VALID(vp->vp_uint32) &&
307  process_state_reply[vp->vp_uint32].send) {
308  request->reply->code = vp->vp_uint32;
309  UPDATE_STATE_CS(reply);
310  break;
311  }
312 
313  RWDEBUG("Ignoring invalid packet-type &reply.%pP", vp);
314  goto update_packet_type;
315 
316  default:
317  MEM(0);
318  }
319 
320  if (cs) RDEBUG("Running '%s %s' from file %s", cf_section_name1(cs), cf_section_name2(cs), cf_filename(cs));
321 
322  return unlang_module_yield_to_section(p_result, request,
323  cs, state->rcode, state->resume,
324  NULL, 0, mctx->rctx);
325 }
326 
327 RESUME(send_generic)
328 {
329  rlm_rcode_t rcode = *p_result;
330  CONF_SECTION *cs;
331  fr_process_state_t const *state;
332  PROCESS_INST const *inst = mctx->mi->data;
333 
335 
336 #ifndef NDEBUG
337  if (!(
339  (request->reply->code == PROCESS_CODE_DO_NOT_RESPOND) ||
340 # endif
341  PROCESS_PACKET_CODE_VALID(request->reply->code))) fr_assert(0);
342 #endif
343  /*
344  * If they delete &reply.Packet-Type, tough for them.
345  */
346  UPDATE_STATE_CS(reply);
347 
349  switch (state->packet_type[rcode]) {
350  case 0: /* don't change the reply */
351  break;
352 
353  default:
354  /*
355  * If we're in the "do not respond" situation,
356  * then don't change the packet code to something
357  * else. However, if we're in (say) Accept, and
358  * the code says Reject, then go do reject.
359  *
360  * The author of the state machine MUST ensure
361  * that there isn't a loop in the state machine
362  * definitions.
363  */
364  if (
366  (request->reply->code != PROCESS_CODE_DO_NOT_RESPOND) &&
367 #endif
368  (state->packet_type[rcode] != request->reply->code)) {
369  char const *old = cf_section_name2(cs);
370 
371  request->reply->code = state->packet_type[rcode];
372  UPDATE_STATE_CS(reply);
373 
374  RWDEBUG("Failed running 'send %s', changing reply to %s", old, cf_section_name2(cs));
375 
376  return unlang_module_yield_to_section(p_result, request,
377  cs, state->rcode, state->send,
378  NULL, 0, mctx->rctx);
379  }
380 
381  fr_assert(!state->packet_type[rcode] || (state->packet_type[rcode] == request->reply->code));
382  break;
383 
384 #ifdef PROCESS_CODE_DO_NOT_RESPOND
386  /*
387  * There might not be send section defined
388  */
389  if (cs) {
390  RDEBUG("The 'send %s' section returned %s - not sending a response",
391  cf_section_name2(cs),
392  fr_table_str_by_value(rcode_table, rcode, "<INVALID>"));
393  }
394  request->reply->code = PROCESS_CODE_DO_NOT_RESPOND;
395  break;
396 #endif
397  }
398 
399  request->reply->timestamp = fr_time();
400 
401 #ifdef PROCESS_CODE_DO_NOT_RESPOND
402  /*
403  * Check for "do not respond".
404  */
405  if (request->reply->code == PROCESS_CODE_DO_NOT_RESPOND) {
406  RDEBUG("Not sending reply to client");
408  }
409 #endif
410 
412 }
413 
414 #ifdef PROCESS_CODE_DYNAMIC_CLIENT
415 RESUME_NO_MCTX(new_client_done)
416 {
417  *p_result = RLM_MODULE_OK;
418 
419  request->reply->timestamp = fr_time();
420 
422 }
423 
424 RESUME(new_client)
425 {
426  rlm_rcode_t rcode = *p_result;
427  CONF_SECTION *cs;
428  PROCESS_INST const *inst = mctx->mi->data;
429 
430  switch (rcode) {
431  case RLM_MODULE_OK:
432  case RLM_MODULE_UPDATED:
433  cs = inst->sections.add_client;
434  request->reply->code = PROCESS_CODE_DYNAMIC_CLIENT;
435  break;
436 
437  default:
438  cs = inst->sections.deny_client;
439  request->reply->code = PROCESS_CODE_DO_NOT_RESPOND;
440  break;
441  }
442  fr_assert(cs != NULL);
443 
444  request->component = NULL;
445  request->module = NULL;
446 
447  RDEBUG("Running '%s %s' from file %s", cf_section_name1(cs), cf_section_name2(cs), cf_filename(cs));
448  return unlang_module_yield_to_section(p_result, request,
449  cs, RLM_MODULE_FAIL, resume_new_client_done,
450  NULL, 0, mctx->rctx);
451 }
452 
453 static inline unlang_action_t new_client(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
454 {
455  CONF_SECTION *cs;
456  PROCESS_INST const *inst = mctx->mi->data;
457 
459  fr_assert(inst->sections.new_client != NULL);
460  cs = inst->sections.new_client;
461 
462  RDEBUG("Running '%s %s' from file %s", cf_section_name1(cs), cf_section_name2(cs), cf_filename(cs));
463  return unlang_module_yield_to_section(p_result, request,
464  cs, RLM_MODULE_FAIL, resume_new_client,
465  NULL, 0, mctx->rctx);
466 }
467 
468 #define DYNAMIC_CLIENT_SECTIONS \
469  { \
470  .section = SECTION_NAME("new", "client"), \
471  .actions = &mod_actions_authorize, \
472  .offset = PROCESS_CONF_OFFSET(new_client), \
473  }, \
474  { \
475  .section = SECTION_NAME("add", "client"), \
476  .actions = &mod_actions_authorize, \
477  .offset = PROCESS_CONF_OFFSET(add_client), \
478  }, \
479  { \
480  .section = SECTION_NAME("deny", "client"), \
481  .actions = &mod_actions_authorize, \
482  .offset = PROCESS_CONF_OFFSET(deny_client), \
483  }
484 
485 #endif /* PROCESS_DYNAMIC_CLIENT */
486 
487 #endif /* defined(PROCESS_INST) && defined(PROCESS_PACKET_TYPE) && defined(PROCESS_PACKET_CODE_VALID) */
488 
489 #ifdef __cplusplus
490 }
491 #endif
unlang_action_t
Returned by unlang_op_t calls, determine the next action of the interpreter.
Definition: action.h:35
@ UNLANG_ACTION_CALCULATE_RESULT
Calculate a new section rlm_rcode_t value.
Definition: action.h:37
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:1185
char const * cf_section_name1(CONF_SECTION const *cs)
Return the second identifier of a CONF_SECTION.
Definition: cf_util.c:1171
#define cf_filename(_cf)
Definition: cf_util.h:107
static fr_dict_attr_t const * attr_packet_type
Definition: dhcpclient.c:89
char const * fr_dict_enum_name_by_value(fr_dict_attr_t const *da, fr_value_box_t const *value)
Lookup the name of an enum value in a fr_dict_attr_t.
Definition: dict_util.c:3382
#define RWDEBUG(fmt,...)
Definition: log.h:361
module_instance_t const * mi
Instance of the module being instantiated.
Definition: module_ctx.h:42
void * rctx
Resume ctx that a module previously set.
Definition: module_ctx.h:45
Temporary structure to hold arguments for module calls.
Definition: module_ctx.h:41
#define PROCESS_PACKET_TYPE
Definition: base.c:61
#define PROCESS_CODE_MAX
Definition: base.c:62
static fr_process_state_t const process_state[]
Definition: base.c:68
#define PROCESS_PACKET_CODE_VALID
Definition: base.c:64
#define PROCESS_INST
Definition: base.c:65
#define PROCESS_CODE_DO_NOT_RESPOND
Definition: base.c:63
static fr_process_state_t const process_state_reply[]
Definition: base.c:197
static fr_process_state_t const process_state_packet[]
Definition: base.c:160
RESUME_NO_MCTX(recv_bfd)
Definition: base.c:121
#define PROCESS_CODE_DYNAMIC_CLIENT
Definition: base.c:130
#define PROCESS_STATE_EXTRA_FIELDS
Definition: base.c:185
RECV(for_any_server)
Validate a solicit/rebind/confirm message.
Definition: base.c:401
fr_dict_t const ** dict
pointer to local fr_dict_t *
Definition: process.h:59
#define PROCESS_TRACE
Trace each state function as it's entered.
Definition: process.h:65
module_method_t process
Process packets.
Definition: process.h:57
virtual_server_compile_t const * compile_list
list of processing sections
Definition: process.h:58
struct fr_process_module_s fr_process_module_t
Common public symbol definition for all process modules.
module_t common
Common fields for all loadable modules.
Definition: process.h:55
Common public symbol definition for all process modules.
Definition: process.h:54
#define REDEBUG(fmt,...)
Definition: radclient.h:52
#define RDEBUG(fmt,...)
Definition: radclient.h:53
fr_table_num_sorted_t const rcode_table[]
Definition: rcode.c:35
rlm_rcode_t
Return codes indicating the result of the module call.
Definition: rcode.h:40
@ RLM_MODULE_OK
The module is OK, continue.
Definition: rcode.h:43
@ RLM_MODULE_FAIL
Module failed, don't reply.
Definition: rcode.h:42
@ RLM_MODULE_UPDATED
OK (pairs modified).
Definition: rcode.h:49
@ RLM_MODULE_NUMCODES
How many valid return codes there are.
Definition: rcode.h:50
static char const * name
void * data
Module's instance data.
Definition: module.h:271
unlang_action_t(* module_method_t)(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
Module section callback.
Definition: module.h:68
Struct exported by a rlm_* module.
Definition: module.h:195
#define pair_update_reply(_attr, _da)
Return or allocate a fr_pair_t in the reply list.
Definition: pair.h:129
#define pair_delete_reply(_pair_or_da)
Delete a fr_pair_t in the reply list.
Definition: pair.h:181
unlang_action_t unlang_module_yield_to_section(rlm_rcode_t *p_result, request_t *request, CONF_SECTION *subcs, rlm_rcode_t default_rcode, module_method_t resume, unlang_module_signal_t signal, fr_signal_t sigmask, void *rctx)
Definition: module.c:248
RETURN_MODULE_FAIL
fr_assert(0)
MEM(pair_append_request(&vp, attr_eap_aka_sim_identity) >=0)
eap_aka_sim_process_conf_t * inst
#define RESUME(_x)
Definition: state_machine.c:51
fr_pair_t * vp
#define fr_time()
Allow us to arbitrarily manipulate time.
Definition: state_test.c:8
Stores an attribute, a value and various bits of other data.
Definition: pair.h:68
#define fr_table_str_by_value(_table, _number, _def)
Convert an integer to a string.
Definition: table.h:772
#define fr_box_uint32(_val)
Definition: value.h:312
Processing sections which are allowed in this virtual server.