The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
base.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: aabf946f382b6fd1184336d848c2f7bf6fc41934 $
19  * @file src/process/dhcpv4/base.c
20  * @brief Base DORA, etc. DHCPV4 processing.
21  *
22  * @copyright 2018 The Freeradius server project.
23  * @copyright 2018 Alan DeKok (aland@deployingradius.com)
24  */
25 #define LOG_PREFIX "process_dhcpv4"
26 
27 #include <freeradius-devel/io/application.h>
28 #include <freeradius-devel/server/protocol.h>
29 #include <freeradius-devel/util/dict.h>
30 #include <freeradius-devel/util/debug.h>
31 #include <freeradius-devel/dhcpv4/dhcpv4.h>
32 
33 static fr_dict_t const *dict_dhcpv4;
34 
37  { .out = &dict_dhcpv4, .proto = "dhcpv4" },
38  { NULL }
39 };
40 
44 
47  { .out = &attr_message_type, .name = "Message-Type", .type = FR_TYPE_UINT8, .dict = &dict_dhcpv4},
48  { .out = &attr_yiaddr, .name = "Your-IP-Address", .type = FR_TYPE_IPV4_ADDR, .dict = &dict_dhcpv4},
49  { .out = &attr_packet_type, .name = "Packet-Type", .type = FR_TYPE_UINT32, .dict = &dict_dhcpv4},
50  { NULL }
51 };
52 
53 /*
54  * Debug the packet if requested.
55  */
56 static void dhcpv4_packet_debug(request_t *request, fr_packet_t *packet, fr_pair_list_t *list, bool received)
57 {
58 #ifdef WITH_IFINDEX_NAME_RESOLUTION
59  char if_name[IFNAMSIZ];
60 #endif
61 
62  if (!packet) return;
63  if (!RDEBUG_ENABLED) return;
64 
65  log_request(L_DBG, L_DBG_LVL_1, request, __FILE__, __LINE__, "%s %s XID %08x from %s%pV%s:%i to %s%pV%s:%i "
66 #ifdef WITH_IFINDEX_NAME_RESOLUTION
67  "%s%s%s"
68 #endif
69  "",
70  received ? "Received" : "Sending",
71  dhcp_message_types[packet->code],
72  packet->id,
73  packet->socket.inet.src_ipaddr.af == AF_INET6 ? "[" : "",
74  fr_box_ipaddr(packet->socket.inet.src_ipaddr),
75  packet->socket.inet.src_ipaddr.af == AF_INET6 ? "]" : "",
76  packet->socket.inet.src_port,
77  packet->socket.inet.dst_ipaddr.af == AF_INET6 ? "[" : "",
78  fr_box_ipaddr(packet->socket.inet.dst_ipaddr),
79  packet->socket.inet.dst_ipaddr.af == AF_INET6 ? "]" : "",
80  packet->socket.inet.dst_port
81 #ifdef WITH_IFINDEX_NAME_RESOLUTION
82  , packet->socket.inet.ifindex ? "via " : "",
83  packet->socket.inet.ifindex ? fr_ifname_from_ifindex(if_name, packet->socket.inet.ifindex) : "",
84  packet->socket.inet.ifindex ? " " : ""
85 #endif
86  );
87 
88  if (received || request->parent) {
89  log_request_pair_list(L_DBG_LVL_1, request, NULL, list, NULL);
90  } else {
91  log_request_proto_pair_list(L_DBG_LVL_1, request, NULL, list, NULL);
92  }
93 }
94 
95 typedef struct {
96  uint64_t nothing; // so that the next field isn't at offset 0
97 
112 
117 
118 typedef struct {
121 
122 #define FR_DHCP_PROCESS_CODE_VALID(_x) (FR_DHCP_PACKET_CODE_VALID(_x) || (_x == FR_DHCP_DO_NOT_RESPOND))
123 
124 #define PROCESS_PACKET_TYPE fr_dhcpv4_packet_code_t
125 #define PROCESS_CODE_MAX FR_DHCP_CODE_MAX
126 #define PROCESS_CODE_DO_NOT_RESPOND FR_DHCP_DO_NOT_RESPOND
127 #define PROCESS_PACKET_CODE_VALID FR_DHCP_PROCESS_CODE_VALID
128 #define PROCESS_INST process_dhcpv4_t
129 #define PROCESS_CODE_DYNAMIC_CLIENT FR_DHCP_ACK
130 #include <freeradius-devel/server/process.h>
131 
132 RESUME(check_yiaddr)
133 {
134  fr_pair_t *vp;
135 
136  vp = fr_pair_find_by_da(&request->reply_pairs, NULL, attr_yiaddr);
137  if (!vp) {
138  REDEBUG("%s packet does not have YIADDR. The client will not receive an IP address.",
139  dhcp_message_types[request->reply->code]);
140  }
141 
142  return CALL_RESUME(send_generic);
143 }
144 
145 static fr_process_state_t const process_state[] = {
146  [FR_DHCP_DISCOVER] = {
147  .packet_type = {
151 
157  },
158  .rcode = RLM_MODULE_NOOP,
159  .default_reply = FR_DHCP_DO_NOT_RESPOND,
160  .recv = recv_generic,
161  .resume = resume_recv_generic,
162  .section_offset = PROCESS_CONF_OFFSET(discover),
163  },
164  [FR_DHCP_OFFER] = {
165  .packet_type = {
169 
175  },
176  .rcode = RLM_MODULE_NOOP,
177  .default_reply = FR_DHCP_DO_NOT_RESPOND,
178  .send = send_generic,
179  .resume = resume_check_yiaddr,
180  .section_offset = PROCESS_CONF_OFFSET(offer),
181  },
182 
183  [FR_DHCP_REQUEST] = {
184  .packet_type = {
188 
194  },
195  .rcode = RLM_MODULE_NOOP,
196  .default_reply = FR_DHCP_DO_NOT_RESPOND,
197  .recv = recv_generic,
198  .resume = resume_recv_generic,
199  .section_offset = PROCESS_CONF_OFFSET(request),
200  },
201  [FR_DHCP_DECLINE] = {
202  .packet_type = {
206 
212  },
213  .rcode = RLM_MODULE_NOOP,
214  .default_reply = FR_DHCP_DO_NOT_RESPOND,
215  .recv = recv_generic,
216  .resume = resume_recv_generic,
217  .section_offset = PROCESS_CONF_OFFSET(request),
218  },
219 
220  [FR_DHCP_ACK] = {
221  .packet_type = {
225 
231  },
232  .rcode = RLM_MODULE_NOOP,
233  .default_reply = FR_DHCP_DO_NOT_RESPOND,
234  .send = send_generic,
235  .resume = resume_check_yiaddr,
236  .section_offset = PROCESS_CONF_OFFSET(ack),
237  },
238  [FR_DHCP_NAK] = {
239  .packet_type = {
243 
249  },
250  .rcode = RLM_MODULE_NOOP,
251  .default_reply = FR_DHCP_DO_NOT_RESPOND,
252  .send = send_generic,
253  .resume = resume_send_generic,
254  .section_offset = PROCESS_CONF_OFFSET(nak),
255  },
256 
257  [FR_DHCP_INFORM] = {
258  .packet_type = {
262 
268  },
269  .rcode = RLM_MODULE_NOOP,
270  .default_reply = FR_DHCP_DO_NOT_RESPOND,
271  .recv = recv_generic,
272  .resume = resume_recv_generic,
273  .section_offset = PROCESS_CONF_OFFSET(inform),
274  },
275 
276  [FR_DHCP_RELEASE] = { /* releases are not responded to */
277  .packet_type = {
281 
287  },
288  .rcode = RLM_MODULE_NOOP,
289  .default_reply = FR_DHCP_DO_NOT_RESPOND,
290  .recv = recv_generic,
291  .resume = resume_recv_generic,
292  .section_offset = PROCESS_CONF_OFFSET(release),
293  },
294 
295  [FR_DHCP_LEASE_QUERY] = {
296  .packet_type = {
300 
306  },
307  .rcode = RLM_MODULE_NOOP,
308  .default_reply = FR_DHCP_DO_NOT_RESPOND,
309  .recv = recv_generic,
310  .resume = resume_recv_generic,
311  .section_offset = PROCESS_CONF_OFFSET(lease_query),
312  },
313 
315  .packet_type = {
319 
325  },
326  .rcode = RLM_MODULE_NOOP,
327  .default_reply = FR_DHCP_DO_NOT_RESPOND,
328  .send = send_generic,
329  .resume = resume_send_generic,
330  .section_offset = PROCESS_CONF_OFFSET(lease_unassigned),
331  },
332 
334  .packet_type = {
338 
344  },
345  .rcode = RLM_MODULE_NOOP,
346  .default_reply = FR_DHCP_DO_NOT_RESPOND,
347  .send = send_generic,
348  .resume = resume_send_generic,
349  .section_offset = PROCESS_CONF_OFFSET(lease_unknown),
350  },
351 
353  .packet_type = {
357 
363  },
364  .rcode = RLM_MODULE_NOOP,
365  .default_reply = FR_DHCP_DO_NOT_RESPOND,
366  .send = send_generic,
367  .resume = resume_send_generic,
368  .section_offset = PROCESS_CONF_OFFSET(lease_active),
369  },
370 
372  .packet_type = {
376 
382  },
383  .rcode = RLM_MODULE_NOOP,
384  .default_reply = FR_DHCP_DO_NOT_RESPOND,
385  .send = send_generic,
386  .resume = resume_send_generic,
387  .section_offset = PROCESS_CONF_OFFSET(do_not_respond),
388  },
389 };
390 
391 static unlang_action_t mod_process(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
392 {
393  fr_process_state_t const *state;
394 
396 
398  fr_assert(PROCESS_PACKET_CODE_VALID(request->packet->code));
399 
400  request->component = "dhcpv4";
401  request->module = NULL;
402  fr_assert(request->dict == dict_dhcpv4);
403 
404  UPDATE_STATE(packet);
405 
406  if (!state->recv) {
407  REDEBUG("Invalid packet type (%u)", request->packet->code);
409  }
410 
411  dhcpv4_packet_debug(request, request->packet, &request->request_pairs, true);
412 
413  if (unlikely(request_is_dynamic_client(request))) {
414  return new_client(p_result, mctx, request);
415  }
416 
417  return state->recv(p_result, mctx, request);
418 }
419 
421  {
422  .name = "recv",
423  .name2 = "Discover",
424  .component = MOD_POST_AUTH,
425 
426  .methods = (const virtual_server_method_t[]) {
427  {
428  .name = "ippool",
429  .name2 = "allocate",
430  },
432  },
433  .offset = PROCESS_CONF_OFFSET(discover),
434  },
435  {
436  .name = "send",
437  .name2 = "Offer",
438  .component = MOD_POST_AUTH,
439  .offset = PROCESS_CONF_OFFSET(offer),
440  },
441  {
442  .name = "recv",
443  .name2 = "Request",
444  .component = MOD_POST_AUTH,
445 
446  .methods = (const virtual_server_method_t[]) {
447  {
448  .name = "ippool",
449  .name2 = "extend",
450  },
452  },
453  .offset = PROCESS_CONF_OFFSET(request),
454  },
455 
456  {
457  .name = "send",
458  .name2 = "Ack",
459  .component = MOD_POST_AUTH,
460  .offset = PROCESS_CONF_OFFSET(ack),
461  },
462  {
463  .name = "send",
464  .name2 = "NAK",
465  .component = MOD_POST_AUTH,
466  .offset = PROCESS_CONF_OFFSET(nak),
467  },
468  {
469  .name = "recv",
470  .name2 = "Decline",
471  .component = MOD_POST_AUTH,
472 
473  .methods = (const virtual_server_method_t[]) {
474  {
475  .name = "ippool",
476  .name2 = "mark",
477  },
479  },
480  .offset = PROCESS_CONF_OFFSET(decline),
481  },
482 
483  {
484  .name = "recv",
485  .name2 = "Release",
486  .component = MOD_POST_AUTH,
487 
488  .methods = (const virtual_server_method_t[]) {
489  {
490  .name = "ippool",
491  .name2 = "release",
492  },
494  },
495  .offset = PROCESS_CONF_OFFSET(release),
496  },
497  {
498  .name = "recv",
499  .name2 = "Inform",
500  .component = MOD_POST_AUTH,
501  .offset = PROCESS_CONF_OFFSET(inform),
502  },
503 
504  {
505  .name = "recv",
506  .name2 = "Lease-Query",
507  .component = MOD_POST_AUTH,
508  .offset = PROCESS_CONF_OFFSET(lease_query),
509  },
510  {
511  .name = "send",
512  .name2 = "Lease-Unassigned",
513  .component = MOD_POST_AUTH,
514  .offset = PROCESS_CONF_OFFSET(lease_unassigned),
515  },
516  {
517  .name = "send",
518  .name2 = "Lease-Unknown",
519  .component = MOD_POST_AUTH,
520  .offset = PROCESS_CONF_OFFSET(lease_unknown),
521  },
522  {
523  .name = "send",
524  .name2 = "Lease-Active",
525  .component = MOD_POST_AUTH,
526  .offset = PROCESS_CONF_OFFSET(lease_active),
527  },
528 
529  {
530  .name = "send",
531  .name2 = "Do-Not-Respond",
532  .component = MOD_POST_AUTH,
533  .offset = PROCESS_CONF_OFFSET(do_not_respond),
534  },
535 
536  DYNAMIC_CLIENT_SECTIONS,
537 
539 };
540 
541 
544  .common = {
545  .magic = MODULE_MAGIC_INIT,
546  .name = "dhcpv4",
547  .inst_size = sizeof(process_dhcpv4_t)
548  },
549  .process = mod_process,
550  .compile_list = compile_list,
551  .dict = &dict_dhcpv4
552 };
unlang_action_t
Returned by unlang_op_t calls, determine the next action of the interpreter.
Definition: action.h:35
#define unlikely(_x)
Definition: build.h:378
A section grouping multiple CONF_PAIR.
Definition: cf_priv.h:89
@ MOD_POST_AUTH
7 methods index for postauth section.
Definition: components.h:37
@ FR_DHCP_REQUEST
Definition: dhcpv4.h:47
@ FR_DHCP_LEASE_UNASSIGNED
Definition: dhcpv4.h:55
@ FR_DHCP_DECLINE
Definition: dhcpv4.h:48
@ FR_DHCP_OFFER
Definition: dhcpv4.h:46
@ FR_DHCP_DISCOVER
Definition: dhcpv4.h:45
@ FR_DHCP_LEASE_ACTIVE
Definition: dhcpv4.h:57
@ FR_DHCP_LEASE_UNKNOWN
Definition: dhcpv4.h:56
@ FR_DHCP_LEASE_QUERY
Definition: dhcpv4.h:54
@ FR_DHCP_NAK
Definition: dhcpv4.h:50
@ FR_DHCP_RELEASE
Definition: dhcpv4.h:51
@ FR_DHCP_DO_NOT_RESPOND
Definition: dhcpv4.h:61
@ FR_DHCP_INFORM
Definition: dhcpv4.h:52
@ FR_DHCP_ACK
Definition: dhcpv4.h:49
fr_dict_attr_t const ** out
Where to write a pointer to the resolved fr_dict_attr_t.
Definition: dict.h:250
fr_dict_t const ** out
Where to write a pointer to the loaded/resolved fr_dict_t.
Definition: dict.h:263
Specifies an attribute which must be present for the module to function.
Definition: dict.h:249
Specifies a dictionary which must be loaded/loadable for the module to function.
Definition: dict.h:262
void *_CONST data
Module instance's parsed configuration.
Definition: dl_module.h:165
#define MODULE_MAGIC_INIT
Stop people using different module/library/server versions together.
Definition: dl_module.h:65
void log_request_proto_pair_list(fr_log_lvl_t lvl, request_t *request, fr_pair_t const *parent, fr_pair_list_t const *vps, char const *prefix)
Print a list of protocol fr_pair_ts.
Definition: log.c:845
void log_request(fr_log_type_t type, fr_log_lvl_t lvl, request_t *request, char const *file, int line, char const *fmt,...)
Marshal variadic log arguments into a va_list and pass to normal logging functions.
Definition: log.c:603
void log_request_pair_list(fr_log_lvl_t lvl, request_t *request, fr_pair_t const *parent, fr_pair_list_t const *vps, char const *prefix)
Print a fr_pair_list_t.
Definition: log.c:821
@ L_DBG_LVL_1
Highest priority debug messages (-x).
Definition: log.h:70
@ L_DBG
Only displayed when debugging is enabled.
Definition: log.h:59
@ FR_TYPE_IPV4_ADDR
32 Bit IPv4 Address.
Definition: merged_model.c:86
@ FR_TYPE_UINT8
8 Bit unsigned integer.
Definition: merged_model.c:97
@ FR_TYPE_UINT32
32 Bit unsigned integer.
Definition: merged_model.c:99
dl_module_inst_t const * inst
Dynamic loader API handle for the module.
Definition: module_ctx.h:42
Temporary structure to hold arguments for module calls.
Definition: module_ctx.h:41
fr_pair_t * fr_pair_find_by_da(fr_pair_list_t const *list, fr_pair_t const *prev, fr_dict_attr_t const *da)
Find the first pair with a matching da.
Definition: pair.c:688
CONF_SECTION * request
Definition: base.c:100
CONF_SECTION * lease_query
Definition: base.c:107
static fr_dict_attr_t const * attr_packet_type
Definition: base.c:43
CONF_SECTION * ack
Definition: base.c:102
static unlang_action_t mod_process(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
Definition: base.c:391
CONF_SECTION * offer
Definition: base.c:99
CONF_SECTION * lease_unassigned
Definition: base.c:108
static void dhcpv4_packet_debug(request_t *request, fr_packet_t *packet, fr_pair_list_t *list, bool received)
Definition: base.c:56
CONF_SECTION * do_not_respond
Definition: base.c:111
CONF_SECTION * nak
Definition: base.c:103
fr_process_module_t process_dhcpv4
Definition: base.c:543
static fr_dict_attr_t const * attr_yiaddr
Definition: base.c:42
CONF_SECTION * force_renew
Definition: base.c:106
CONF_SECTION * decline
Definition: base.c:101
CONF_SECTION * new_client
Definition: base.c:113
static const virtual_server_compile_t compile_list[]
Definition: base.c:420
fr_dict_attr_autoload_t process_dhcpv4_dict_attr[]
Definition: base.c:46
static fr_dict_t const * dict_dhcpv4
Definition: base.c:33
fr_dict_autoload_t process_dhcpv4_dict[]
Definition: base.c:36
static fr_process_state_t const process_state[]
Definition: base.c:145
CONF_SECTION * add_client
Definition: base.c:114
CONF_SECTION * inform
Definition: base.c:105
CONF_SECTION * lease_active
Definition: base.c:110
process_dhcpv4_sections_t sections
Definition: base.c:119
CONF_SECTION * release
Definition: base.c:104
CONF_SECTION * discover
Definition: base.c:98
CONF_SECTION * deny_client
Definition: base.c:115
RESUME(check_yiaddr)
Definition: base.c:132
#define PROCESS_PACKET_CODE_VALID
Definition: base.c:127
CONF_SECTION * lease_unknown
Definition: base.c:109
static fr_dict_attr_t const * attr_message_type
Definition: base.c:41
#define PROCESS_TRACE
Trace each state function as it's entered.
Definition: process.h:65
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
char const * dhcp_message_types[]
Definition: base.c:124
#define REDEBUG(fmt,...)
Definition: radclient.h:52
#define RDEBUG_ENABLED()
Definition: radclient.h:49
rlm_rcode_t
Return codes indicating the result of the module call.
Definition: rcode.h:40
@ RLM_MODULE_INVALID
The module considers the request invalid.
Definition: rcode.h:45
@ 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_DISALLOW
Reject the request (user is locked out).
Definition: rcode.h:46
@ RLM_MODULE_REJECT
Immediately reject the request.
Definition: rcode.h:41
@ RLM_MODULE_NOTFOUND
User not found.
Definition: rcode.h:47
@ RLM_MODULE_UPDATED
OK (pairs modified).
Definition: rcode.h:49
@ RLM_MODULE_NOOP
Module succeeded without doing anything.
Definition: rcode.h:48
#define request_is_dynamic_client(_x)
Definition: request.h:162
RETURN_MODULE_FAIL
fr_assert(0)
fr_pair_t * vp
Stores an attribute, a value and various bits of other data.
Definition: pair.h:68
#define talloc_get_type_abort_const
Definition: talloc.h:270
unsigned int code
Packet code (type).
Definition: packet.h:61
fr_socket_t socket
This packet was received on.
Definition: packet.h:57
int id
Packet ID (used to link requests/responses).
Definition: packet.h:60
int af
AF_INET, AF_INET6, or AF_UNIX.
Definition: socket.h:78
#define fr_box_ipaddr(_val)
Definition: value.h:287
#define COMPILE_TERMINATOR
char const * name
Name of the processing section, such as "recv" or "send".
Processing sections which are allowed in this virtual server.
Module methods which are allowed in virtual servers.