The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
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: 9dd1657cea12bea3956150b6b66eb25a335d3313 $
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
46extern "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 */
54typedef 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 *
60 fr_dict_attr_t const **packet_type; //!< Request packet types to look for finally sections for.
62
63/** Trace each state function as it's entered
64 */
65#ifndef NDEBUG
66# define PROCESS_TRACE RDEBUG3("Entered state %s", __FUNCTION__)
67#else
68# define PROCESS_TRACE
69#endif
70
71/** Convenience macro for providing CONF_SECTION offsets in section compilation arrays
72 *
73 */
74#ifdef PROCESS_INST
75# define PROCESS_CONF_OFFSET(_x) offsetof(PROCESS_INST, sections._x)
76#endif
77
78#if defined(PROCESS_INST) && defined(PROCESS_PACKET_TYPE) && defined(PROCESS_PACKET_CODE_VALID)
79typedef PROCESS_PACKET_TYPE fr_process_rcode_t[RLM_MODULE_NUMCODES];
80
81#ifndef PROCESS_STATE_EXTRA_FIELDS
82# define PROCESS_STATE_EXTRA_FIELDS
83#endif
84
85/*
86 * Process state machine tables for rcode to packet.
87 */
88typedef struct {
89 PROCESS_PACKET_TYPE packet_type[RLM_MODULE_NUMCODES]; //!< rcode to packet type mapping.
90 PROCESS_PACKET_TYPE default_reply; //!< if not otherwise set
91 size_t section_offset; //!< Where to look in the process instance for
92 ///< a pointer to the section we should execute.
93 rlm_rcode_t rcode; //!< Default rcode
94 module_method_t resume; //!< Function to call after running a recv section.
95
96 /*
97 * Each state has only one "recv" or "send".
98 */
99 union {
100 module_method_t recv; //!< Method to call when receiving this type of packet.
101 module_method_t send; //!< Method to call when sending this type of packet.
102 };
104} fr_process_state_t;
105
106/*
107 * Some protocols have the same packet codes for requests and replies.
108 */
109#ifndef PROCESS_SEND_RECV
110#define process_state_packet process_state
111#define process_state_reply process_state
112static fr_process_state_t const process_state[];
113#else
114static fr_process_state_t const process_state_packet[];
115static fr_process_state_t const process_state_reply[];
116#endif
117
118/*
119 * Process state machine functions
120 */
121#define UPDATE_STATE_CS(_x) \
122do { \
123 state = &process_state_ ## _x[request->_x->code]; \
124 memcpy(&cs, (CONF_SECTION * const *) (((uint8_t const *) &inst->sections) + state->section_offset), sizeof(cs)); \
125} while (0)
126
127#define UPDATE_STATE(_x) state = &process_state_ ## _x [request->_x->code]
128
129#define RECV(_x) static inline unlang_action_t recv_ ## _x(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
130#define SEND(_x) static inline unlang_action_t send_ ## _x(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
131#define RESUME(_x) static inline unlang_action_t resume_ ## _x(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
132#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)
133
134/** Call a module method with a new rctx
135 *
136 * @note This should be used to add a rctxs when calling the initial recv section.
137 *
138 * @param[out] p_result Pointer to the result code.
139 * @param[in] mctx Module context.
140 * @param[in] request Request.
141 * @param[in] method Method to call.
142 * @param[in] rctx Resume context to use to override the one in the mctx.
143 * @return Result of the method call.
144 */
145static inline CC_HINT(always_inline)
146unlang_action_t process_with_rctx(rlm_rcode_t *p_result, module_ctx_t const *mctx,
147 request_t *request, module_method_t method, void *rctx)
148{
149 module_ctx_t our_mctx = *mctx;
150 our_mctx.rctx = rctx;
151
152 return method(p_result, &our_mctx, request);
153}
154
155/** Call a named recv function directly
156 */
157#define CALL_RECV(_x) recv_ ## _x(p_result, mctx, request)
158
159/** Call a named recv function directly with a new rctx
160 */
161#define CALL_RECV_RCTX(_x, _rctx) process_with_rctx(p_result, mctx, request, recv_ ## _x, _rctx);
162
163/** Call a named send function directly
164 */
165#define CALL_SEND(_x) send_ ## _x(p_result, mctx, request)
166
167/** Call a named resume function directly
168 */
169#define CALL_RESUME(_x) resume_ ## _x(p_result, mctx, request)
170
171/** Call the send function for the current state
172 */
173#define CALL_SEND_STATE(_state) state->send(p_result, mctx, request)
174
175/** Set the current reply code, and call the send function for that state
176 */
177#define CALL_SEND_TYPE(_x) call_send_type(process_state_reply[(request->reply->code = _x)].send, p_result, mctx, request)
178
179static inline unlang_action_t call_send_type(module_method_t send, \
180 rlm_rcode_t *p_result, module_ctx_t const *mctx,
181 request_t *request)
182{
183 /*
184 * Stupid hack to stop this being honoured
185 * by send_generic.
186 */
188 return send(p_result, mctx, request);
189}
190
191RECV(generic)
192{
193 CONF_SECTION *cs;
194 fr_process_state_t const *state;
195 PROCESS_INST const *inst = mctx->mi->data;
196
198
199 UPDATE_STATE_CS(packet);
200
201 if (!state->recv) {
202 char const *name;
203
205 if (name) {
206 REDEBUG("Invalid packet type (%s)", name);
207 } else {
208 REDEBUG("Invalid packet type (%u)", request->packet->code);
209 }
211 }
212
213
214 if (cs) RDEBUG("Running '%s %s' from file %s", cf_section_name1(cs), cf_section_name2(cs), cf_filename(cs));
215 return unlang_module_yield_to_section(p_result, request,
216 cs, state->rcode, state->resume,
217 NULL, 0, mctx->rctx);
218}
219
220RESUME(recv_generic)
221{
222 rlm_rcode_t rcode = *p_result;
223 fr_process_state_t const *state;
224
226
228
229 UPDATE_STATE(packet);
230
231 request->reply->code = state->packet_type[rcode];
232 if (!request->reply->code) request->reply->code = state->default_reply;
233#ifdef PROCESS_CODE_DO_NOT_RESPOND
234 if (!request->reply->code) request->reply->code = PROCESS_CODE_DO_NOT_RESPOND;
235
236#endif
237 fr_assert(PROCESS_PACKET_CODE_VALID(request->reply->code));
238
239 UPDATE_STATE(reply);
240 fr_assert(state->send != NULL);
241 return state->send(p_result, mctx, request);
242}
243
244RESUME_NO_MCTX(recv_no_send)
245{
246 rlm_rcode_t rcode = *p_result;
247 fr_process_state_t const *state;
248
250
252
253 UPDATE_STATE(packet);
254
255 request->reply->code = state->packet_type[rcode];
256 if (!request->reply->code) request->reply->code = state->default_reply;
257#ifdef PROCESS_CODE_DO_NOT_RESPOND
258 if (!request->reply->code) request->reply->code = PROCESS_CODE_DO_NOT_RESPOND;
259
260#endif
261 fr_assert(request->reply->code != 0);
262
264}
265
266SEND(generic)
267{
268 fr_pair_t *vp;
269 CONF_SECTION *cs;
270 fr_process_state_t const *state;
271 PROCESS_INST const *inst = mctx->mi->data;
272
274
275#ifndef NDEBUG
276 if (!(
278 (request->reply->code == PROCESS_CODE_DO_NOT_RESPOND) ||
279# endif
280 PROCESS_PACKET_CODE_VALID(request->reply->code))) fr_assert(0);
281#endif
282
283 UPDATE_STATE_CS(reply);
284
285 /*
286 * Allow for over-ride of reply code, IF it's
287 * within range, AND we've pre-compiled the
288 * unlang.
289 *
290 * Add reply->packet-type in case we're
291 * being called via the `call {}` keyword.
292 *
293 * @todo - enforce that this is an allowed reply for the
294 * request.
295 */
297 case 0: /* Does not exist */
298 update_packet_type:
299 vp->vp_uint32 = request->reply->code;
300 break;
301
302 case 1: /* Exists */
303 if (
304#ifdef PROCESS_CODE_MAX
305 (vp->vp_uint32 != PROCESS_CODE_MAX) &&
306#endif
307 PROCESS_PACKET_CODE_VALID(vp->vp_uint32) &&
308 process_state_reply[vp->vp_uint32].send) {
309 request->reply->code = vp->vp_uint32;
310 UPDATE_STATE_CS(reply);
311 break;
312 }
313
314 RWDEBUG("Ignoring invalid packet-type reply.%pP", vp);
315 goto update_packet_type;
316
317 default:
318 MEM(0);
319 }
320
321 if (cs) {
322 RDEBUG("Running '%s %s' from file %s", cf_section_name1(cs), cf_section_name2(cs), cf_filename(cs));
323 } else {
324 char const *name;
325
327 if (name) {
328 RWDEBUG("No 'send %s { ... } section was found.", name);
329 } else {
330 RWDEBUG("No 'send %u { ... } section was found.", request->reply->code);
331 }
332 }
333
334 return unlang_module_yield_to_section(p_result, request,
335 cs, state->rcode, state->resume,
336 NULL, 0, mctx->rctx);
337}
338
339RESUME(send_generic)
340{
341 rlm_rcode_t rcode = *p_result;
342 CONF_SECTION *cs;
343 fr_process_state_t const *state;
344 PROCESS_INST const *inst = mctx->mi->data;
345
347
348#ifndef NDEBUG
349 if (!(
351 (request->reply->code == PROCESS_CODE_DO_NOT_RESPOND) ||
352# endif
353 PROCESS_PACKET_CODE_VALID(request->reply->code))) fr_assert(0);
354#endif
355 /*
356 * If they delete &reply.Packet-Type, tough for them.
357 */
358 UPDATE_STATE_CS(reply);
359
361 switch (state->packet_type[rcode]) {
362 case 0: /* don't change the reply */
363 break;
364
365 default:
366 /*
367 * If we're in the "do not respond" situation,
368 * then don't change the packet code to something
369 * else. However, if we're in (say) Accept, and
370 * the code says Reject, then go do reject.
371 *
372 * The author of the state machine MUST ensure
373 * that there isn't a loop in the state machine
374 * definitions.
375 */
376 if (
378 (request->reply->code != PROCESS_CODE_DO_NOT_RESPOND) &&
379#endif
380 (state->packet_type[rcode] != request->reply->code)) {
381 char const *old = cf_section_name2(cs);
382
383 request->reply->code = state->packet_type[rcode];
384 UPDATE_STATE_CS(reply);
385
386 RWDEBUG("Failed running 'send %s', changing reply to %s", old, cf_section_name2(cs));
387
388 return unlang_module_yield_to_section(p_result, request,
389 cs, state->rcode, state->send,
390 NULL, 0, mctx->rctx);
391 }
392
393 fr_assert(!state->packet_type[rcode] || (state->packet_type[rcode] == request->reply->code));
394 break;
395
396#ifdef PROCESS_CODE_DO_NOT_RESPOND
398 /*
399 * There might not be send section defined
400 */
401 if (cs) {
402 RDEBUG("The 'send %s' section returned %s - not sending a response",
404 fr_table_str_by_value(rcode_table, rcode, "<INVALID>"));
405 }
406 request->reply->code = PROCESS_CODE_DO_NOT_RESPOND;
407 break;
408#endif
409 }
410
411 request->reply->timestamp = fr_time();
412
413#ifdef PROCESS_CODE_DO_NOT_RESPOND
414 /*
415 * Check for "do not respond".
416 */
417 if (request->reply->code == PROCESS_CODE_DO_NOT_RESPOND) {
418 RDEBUG("Not sending reply to client");
420 }
421#endif
422
424}
425
426#ifdef PROCESS_CODE_DYNAMIC_CLIENT
427RESUME_NO_MCTX(new_client_done)
428{
429 *p_result = RLM_MODULE_OK;
430
431 request->reply->timestamp = fr_time();
432
434}
435
436RESUME(new_client)
437{
438 rlm_rcode_t rcode = *p_result;
439 CONF_SECTION *cs;
440 PROCESS_INST const *inst = mctx->mi->data;
441
442 switch (rcode) {
443 case RLM_MODULE_OK:
445 RDEBUG("new client was successful.");
446 cs = inst->sections.add_client;
447 request->reply->code = PROCESS_CODE_DYNAMIC_CLIENT;
448 break;
449
450 default:
451 RDEBUG("new client was denied.");
452 cs = inst->sections.deny_client;
453 request->reply->code = PROCESS_CODE_DO_NOT_RESPOND;
454 break;
455 }
456
457 request->component = NULL;
458 request->module = NULL;
459
460 if (!cs) {
461 *p_result = RLM_MODULE_OK;
462 request->reply->timestamp = fr_time();
464 }
465
466 RDEBUG("Running '%s %s' from file %s", cf_section_name1(cs), cf_section_name2(cs), cf_filename(cs));
467 return unlang_module_yield_to_section(p_result, request,
468 cs, RLM_MODULE_FAIL, resume_new_client_done,
469 NULL, 0, mctx->rctx);
470}
471
472static inline unlang_action_t new_client(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
473{
474 CONF_SECTION *cs;
475 PROCESS_INST const *inst = mctx->mi->data;
476
478 fr_assert(inst->sections.new_client != NULL);
479 cs = inst->sections.new_client;
480
481 RDEBUG("Running '%s %s' from file %s", cf_section_name1(cs), cf_section_name2(cs), cf_filename(cs));
482 return unlang_module_yield_to_section(p_result, request,
483 cs, RLM_MODULE_FAIL, resume_new_client,
484 NULL, 0, mctx->rctx);
485}
486
487#define DYNAMIC_CLIENT_SECTIONS \
488 { \
489 .section = SECTION_NAME("new", "client"), \
490 .actions = &mod_actions_authorize, \
491 .offset = PROCESS_CONF_OFFSET(new_client), \
492 }, \
493 { \
494 .section = SECTION_NAME("add", "client"), \
495 .actions = &mod_actions_authorize, \
496 .offset = PROCESS_CONF_OFFSET(add_client), \
497 }, \
498 { \
499 .section = SECTION_NAME("deny", "client"), \
500 .actions = &mod_actions_authorize, \
501 .offset = PROCESS_CONF_OFFSET(deny_client), \
502 }
503
504#endif /* PROCESS_DYNAMIC_CLIENT */
505
506#endif /* defined(PROCESS_INST) && defined(PROCESS_PACKET_TYPE) && defined(PROCESS_PACKET_CODE_VALID) */
507
508#ifdef __cplusplus
509}
510#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:1184
char const * cf_section_name1(CONF_SECTION const *cs)
Return the second identifier of a CONF_SECTION.
Definition cf_util.c:1170
#define cf_filename(_cf)
Definition cf_util.h:107
#define MEM(x)
Definition debug.h:36
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:3426
#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:66
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
fr_dict_attr_t const ** packet_type
Request packet types to look for finally sections for.
Definition process.h:60
Common public symbol definition for all process modules.
Definition process.h:54
#define fr_assert(_expr)
Definition rad_assert.h:38
#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
#define RETURN_MODULE_FAIL
Definition rcode.h:57
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:51
static char const * name
void * data
Module's instance data.
Definition module.h:272
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:247
eap_aka_sim_process_conf_t * inst
#define RESUME(_x)
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:331
Processing sections which are allowed in this virtual server.