The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
rlm_dhcpv4.c
Go to the documentation of this file.
1 /*
2  * This program is 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 (at
5  * 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: 87c14390c62c87bc45580335ac3bcdb85566515a $
19  * @file src/modules/rlm_dhcpv4/rlm_dhcpv4.c
20  * @brief DHCP client and relay
21  *
22  * @copyright 2012-2018 The FreeRADIUS server project
23  */
24 RCSID("$Id: 87c14390c62c87bc45580335ac3bcdb85566515a $")
25 
26 #include <freeradius-devel/server/base.h>
27 #include <freeradius-devel/server/module_rlm.h>
28 #include <freeradius-devel/io/pair.h>
29 #include <freeradius-devel/util/udp_queue.h>
30 #include <freeradius-devel/dhcpv4/dhcpv4.h>
31 
32 #include <freeradius-devel/unlang/module.h>
33 
34 static fr_dict_t const *dict_dhcpv4;
35 static fr_dict_t const *dict_freeradius;
36 
39  { .out = &dict_dhcpv4, .proto = "dhcpv4" },
40  { .out = &dict_freeradius, .proto = "freeradius" },
41  { NULL }
42 };
43 
50 
53  { .out = &attr_transaction_id, .name = "Transaction-Id", .type = FR_TYPE_UINT32, .dict = &dict_dhcpv4 },
54  { .out = &attr_gateway_ip_address, .name = "Gateway-IP-Address", .type = FR_TYPE_IPV4_ADDR, .dict = &dict_dhcpv4 },
55  { .out = &attr_message_type, .name = "Message-Type", .type = FR_TYPE_UINT8, .dict = &dict_dhcpv4 },
56  { .out = &attr_packet_type, .name = "Packet-Type", .type = FR_TYPE_UINT32, .dict = &dict_dhcpv4 },
57  { .out = &attr_net_dst_ip, .name = "Net.Dst.IP", .type = FR_TYPE_COMBO_IP_ADDR, .dict = &dict_freeradius },
58  { .out = &attr_net_dst_port, .name = "Net.Dst.Port", .type = FR_TYPE_UINT16, .dict = &dict_freeradius },
59  { NULL }
60 };
61 
62 
63 /*
64  * Define a structure for our module configuration.
65  *
66  * These variables do not need to be in a structure, but it's
67  * a lot cleaner to do so, and a pointer to the structure can
68  * be used as the instance handle.
69  */
70 typedef struct {
71  fr_udp_queue_config_t config; //!< UDP queue config
72 
73  uint32_t max_packet_size; //!< Maximum packet size.
74 } rlm_dhcpv4_t;
75 
76 typedef struct {
77  fr_udp_queue_t *uq; //!< udp queue handler
78  uint8_t *buffer; //!< for encoding packets
79  uint32_t buffer_size; //!< Maximum packet size.
80  uint32_t xid; //!< XID
82 
83 static const conf_parser_t module_config[] = {
86 
87  { FR_CONF_OFFSET("port", rlm_dhcpv4_t, config.port), .dflt = "68" },
88 
89  { FR_CONF_OFFSET("interface", rlm_dhcpv4_t, config.interface) },
90 
91  { FR_CONF_OFFSET_IS_SET("send_buff", FR_TYPE_UINT32, 0, rlm_dhcpv4_t, config.send_buff) },
92 
93  { FR_CONF_OFFSET("max_packet_size", rlm_dhcpv4_t, max_packet_size), .dflt = "576" },
94  { FR_CONF_OFFSET("max_queued_packets", rlm_dhcpv4_t, config.max_queued_packets), .dflt = "65536" },
95 
96  { FR_CONF_OFFSET("timeout", rlm_dhcpv4_t, config.max_queued_time), .dflt = "0" },
97 
99 };
100 
101 typedef struct {
103  bool sent;
105 
106 static void dhcpv4_queue_resume(bool sent, void *rctx)
107 {
108  rlm_dhcpv4_delay_t *d = talloc_get_type_abort(rctx, rlm_dhcpv4_delay_t);
109 
110  d->sent = sent;
111 
113 }
114 
115 static unlang_action_t dhcpv4_resume(rlm_rcode_t *p_result, module_ctx_t const *mctx, UNUSED request_t *request)
116 {
117  rlm_dhcpv4_delay_t *d = talloc_get_type_abort(mctx->rctx, rlm_dhcpv4_delay_t);
118 
119  if (!d->sent) {
120  talloc_free(d);
122  }
123 
124  talloc_free(d);
126 }
127 
128 /** Send packets outbound.
129  *
130  */
131 static unlang_action_t CC_HINT(nonnull) mod_process(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
132 {
133  rlm_dhcpv4_thread_t *t = talloc_get_type_abort(mctx->thread, rlm_dhcpv4_thread_t);
134  ssize_t data_len;
135  dhcp_packet_t *original = (dhcp_packet_t *) request->packet->data;
136  dhcp_packet_t *packet;
137 
138  uint32_t xid;
139  fr_pair_t *vp;
140  int code, port, rcode;
141 
143 
144  /*
145  * We can only send relayed packets, which have a gateway IP
146  */
147  vp = fr_pair_find_by_da(&request->request_pairs, NULL, attr_gateway_ip_address);
148  if (!vp) {
149  REDEBUG("Relayed packets MUST have a Gateway-IP-Address attribute");
151  }
152 
153  /*
154  * Get the transaction ID.
155  */
156  vp = fr_pair_find_by_da(&request->request_pairs, NULL, attr_transaction_id);
157  if (vp) {
158  xid = vp->vp_uint32;
159 
160  } else if (original) {
161  xid = ntohl(original->xid);
162 
163  } else {
164  xid = t->xid++;
165  }
166 
167  /*
168  * Set the packet type.
169  *
170  * @todo - make sure it's a client type.
171  */
172  vp = fr_pair_find_by_da(&request->request_pairs, NULL, attr_packet_type);
173  if (vp) {
174  code = vp->vp_uint32;
175 
176  } else if ((vp = fr_pair_find_by_da(&request->request_pairs, NULL, attr_message_type)) != NULL) {
177  code = vp->vp_uint8;
178 
179  } else {
180  code = request->packet->code;
181  }
182 
183  /*
184  * Set the destination port, defaulting to 67
185  */
186  vp = fr_pair_find_by_da_nested(&request->control_pairs, NULL, attr_net_dst_port);
187  if (vp) {
188  port = vp->vp_uint16;
189  } else {
190  port = 67; /* DHCPv4 server port */
191  }
192 
193  /*
194  * Get the destination address / port, and unicast it there.
195  */
196  vp = fr_pair_find_by_da_nested(&request->control_pairs, NULL, attr_net_dst_ip);
197  if (!vp || (vp->vp_ip.af != AF_INET)) {
198  RDEBUG("No &control.Net.Dst.IP, cannot relay packet");
200  }
201 
202  /*
203  * Encode the packet using the original information.
204  */
205  data_len = fr_dhcpv4_encode(t->buffer, t->buffer_size, original, code, xid, &request->request_pairs);
206  if (data_len <= 0) {
207  RPEDEBUG("Failed encoding DHCPV4 request");
209  }
210 
211  /*
212  * Enforce some other RFC requirements.
213  */
214  packet = (dhcp_packet_t *) t->buffer;
215  if (packet->opcode == 1) {
216  if (original) {
217  if (original->hops < 255) packet->hops = original->hops + 1;
218  } else {
219  if (packet->hops < 255) packet->hops++;
220  }
221 
222  } /* else sending a server message? OK boomer. */
223 
224  FR_PROTO_HEX_DUMP(t->buffer, data_len, "DHCPv4");
225 
226  d = talloc_zero(request, rlm_dhcpv4_delay_t);
227  if (!d) RETURN_MODULE_FAIL;
228 
229  *d = (rlm_dhcpv4_delay_t) {
230  .request = request,
231  .sent = false,
232  };
233 
234  if (RDEBUG_ENABLED) {
235  RDEBUG("Sending %s XID %08X to %pV:%d", dhcp_message_types[code], request->packet->id, &vp->data, port);
236  RINDENT();
237  log_request_pair_list(L_DBG_LVL_1, request, NULL, &request->request_pairs, NULL);
238  REXDENT();
239  }
240 
241  rcode = fr_udp_queue_write(d, t->uq, t->buffer, data_len, &vp->vp_ip, port, d);
242  if (rcode > 0) {
243  talloc_free(d);
245  }
246  if (rcode < 0) {
247  talloc_free(d);
249  }
250 
251  return unlang_module_yield(request, dhcpv4_resume, NULL, 0, d);
252 }
253 
254 
255 /** Instantiate thread data for the submodule.
256  *
257  */
259 {
260  rlm_dhcpv4_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_dhcpv4_t);
261  rlm_dhcpv4_thread_t *t = talloc_get_type_abort(mctx->thread, rlm_dhcpv4_thread_t);
262  CONF_SECTION *conf = mctx->mi->conf;
263 
264  t->buffer = talloc_array(t, uint8_t, inst->max_packet_size);
265  if (!t->buffer) {
266  cf_log_err(conf, "Failed allocating buffer");
267  return -1;
268  }
269 
270  t->buffer_size = inst->max_packet_size;
271 
272  t->uq = fr_udp_queue_alloc(t, &inst->config, mctx->el, dhcpv4_queue_resume);
273  if (!t->uq) {
274  cf_log_err(conf, "Failed allocating outbound udp queue - %s", fr_strerror());
275  return -1;
276  }
277 
278  return 0;
279 }
280 
281 /** Bootstrap the module
282  *
283  * Bootstrap I/O and type submodules.
284  *
285  * @param[in] mctx data for this module
286  * @return
287  * - 0 on success.
288  * - -1 on failure.
289  */
290 static int mod_instantiate(module_inst_ctx_t const *mctx)
291 {
292  rlm_dhcpv4_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_dhcpv4_t);
293  CONF_SECTION *conf = mctx->mi->conf;
294 
295  /*
296  * Ensure that we have a destination address.
297  */
298  if (inst->config.ipaddr.af == AF_UNSPEC) {
299  cf_log_err(conf, "A value must be given for 'ipaddr'");
300  return -1;
301  }
302 
303  if (inst->config.ipaddr.af != AF_INET) {
304  cf_log_err(conf, "DHCPv4 can only use IPv4 addresses in 'ipaddr'");
305  return -1;
306  }
307 
308  if (!inst->config.port) {
309  cf_log_err(conf, "A value must be given for 'port'");
310  return -1;
311  }
312 
313  /*
314  * Clamp max_packet_size
315  */
316  FR_INTEGER_BOUND_CHECK("max_packet_size", inst->max_packet_size, >=, DEFAULT_PACKET_SIZE);
317  FR_INTEGER_BOUND_CHECK("max_packet_size", inst->max_packet_size, <=, MAX_PACKET_SIZE);
318 
319  return 0;
320 }
321 
322 extern module_rlm_t rlm_dhcpv4;
324  .common = {
325  .magic = MODULE_MAGIC_INIT,
326  .name = "dhcpv4",
327  .inst_size = sizeof(rlm_dhcpv4_t),
329 
331 
332  .thread_inst_size = sizeof(rlm_dhcpv4_thread_t),
333  .thread_inst_type = "rlm_dhcpv4_thread_t",
334  .thread_instantiate = mod_thread_instantiate
335  },
336  .method_group = {
337  .bindings = (module_method_binding_t[]){
338  { .section = SECTION_NAME(CF_IDENT_ANY, CF_IDENT_ANY), .method = mod_process },
340  }
341  }
342 };
unlang_action_t
Returned by unlang_op_t calls, determine the next action of the interpreter.
Definition: action.h:35
#define RCSID(id)
Definition: build.h:481
#define UNUSED
Definition: build.h:313
#define CONF_PARSER_TERMINATOR
Definition: cf_parse.h:627
#define FR_INTEGER_BOUND_CHECK(_name, _var, _op, _bound)
Definition: cf_parse.h:487
#define FR_CONF_OFFSET(_name, _struct, _field)
conf_parser_t which parses a single CONF_PAIR, writing the result to a field in a struct
Definition: cf_parse.h:268
#define FR_CONF_OFFSET_IS_SET(_name, _type, _flags, _struct, _field)
conf_parser_t which parses a single CONF_PAIR, writing the result to a field in a struct,...
Definition: cf_parse.h:282
#define FR_CONF_OFFSET_TYPE_FLAGS(_name, _type, _flags, _struct, _field)
conf_parser_t which parses a single CONF_PAIR, writing the result to a field in a struct
Definition: cf_parse.h:241
Defines a CONF_PAIR to C data type mapping.
Definition: cf_parse.h:564
A section grouping multiple CONF_PAIR.
Definition: cf_priv.h:101
#define cf_log_err(_cf, _fmt,...)
Definition: cf_util.h:289
#define CF_IDENT_ANY
Definition: cf_util.h:78
#define DEFAULT_PACKET_SIZE
Definition: dhcpv4.h:89
#define MAX_PACKET_SIZE
Definition: dhcpv4.h:90
uint8_t hops
Definition: dhcpv4.h:70
uint32_t xid
Definition: dhcpv4.h:71
fr_dict_attr_t const ** out
Where to write a pointer to the resolved fr_dict_attr_t.
Definition: dict.h:267
fr_dict_t const ** out
Where to write a pointer to the loaded/resolved fr_dict_t.
Definition: dict.h:280
Specifies an attribute which must be present for the module to function.
Definition: dict.h:266
Specifies a dictionary which must be loaded/loadable for the module to function.
Definition: dict.h:279
#define MODULE_MAGIC_INIT
Stop people using different module/library/server versions together.
Definition: dl_module.h:63
if(rcode > 0)
Definition: fd_read.h:9
void unlang_interpret_mark_runnable(request_t *request)
Mark a request as resumable.
Definition: interpret.c:1359
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:830
#define REXDENT()
Exdent (unindent) R* messages by one level.
Definition: log.h:443
#define RPEDEBUG(fmt,...)
Definition: log.h:376
#define RINDENT()
Indent R* messages by one level.
Definition: log.h:430
talloc_free(reap)
@ L_DBG_LVL_1
Highest priority debug messages (-x).
Definition: log.h:70
@ FR_TYPE_IPV4_ADDR
32 Bit IPv4 Address.
Definition: merged_model.c:86
@ 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_COMBO_IP_ADDR
IPv4 or IPv6 address depending on length.
Definition: merged_model.c:91
unsigned int uint32_t
Definition: merged_model.c:33
long int ssize_t
Definition: merged_model.c:24
unsigned char uint8_t
Definition: merged_model.c:30
void * thread
Thread specific instance data.
Definition: module_ctx.h:43
void * rctx
Resume ctx that a module previously set.
Definition: module_ctx.h:45
fr_event_list_t * el
Event list to register any IO handlers and timers against.
Definition: module_ctx.h:68
void * thread
Thread instance data.
Definition: module_ctx.h:67
module_instance_t const * mi
Instance of the module being instantiated.
Definition: module_ctx.h:64
module_instance_t * mi
Instance of the module being instantiated.
Definition: module_ctx.h:51
Temporary structure to hold arguments for module calls.
Definition: module_ctx.h:41
Temporary structure to hold arguments for instantiation calls.
Definition: module_ctx.h:50
Temporary structure to hold arguments for thread_instantiation calls.
Definition: module_ctx.h:63
module_t common
Common fields presented by all modules.
Definition: module_rlm.h:39
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:693
fr_pair_t * fr_pair_find_by_da_nested(fr_pair_list_t const *list, fr_pair_t const *prev, fr_dict_attr_t const *da)
Find a pair with a matching fr_dict_attr_t, by walking the nested fr_dict_attr_t tree.
Definition: pair.c:770
static const conf_parser_t config[]
Definition: base.c:183
char const * dhcp_message_types[]
Definition: base.c:126
ssize_t fr_dhcpv4_encode(uint8_t *buffer, size_t buflen, dhcp_packet_t *original, int code, uint32_t xid, fr_pair_list_t *vps)
Definition: base.c:336
#define REDEBUG(fmt,...)
Definition: radclient.h:52
#define RDEBUG(fmt,...)
Definition: radclient.h:53
#define RDEBUG_ENABLED()
Definition: radclient.h:49
static rs_t * conf
Definition: radsniff.c:53
#define RETURN_MODULE_NOOP
Definition: rcode.h:62
#define RETURN_MODULE_OK
Definition: rcode.h:57
rlm_rcode_t
Return codes indicating the result of the module call.
Definition: rcode.h:40
static void dhcpv4_queue_resume(bool sent, void *rctx)
Definition: rlm_dhcpv4.c:106
uint32_t max_packet_size
Maximum packet size.
Definition: rlm_dhcpv4.c:73
module_rlm_t rlm_dhcpv4
Definition: rlm_dhcpv4.c:323
uint32_t xid
XID.
Definition: rlm_dhcpv4.c:80
fr_dict_attr_autoload_t rlm_dhcpv4_dict_attr[]
Definition: rlm_dhcpv4.c:52
static fr_dict_attr_t const * attr_packet_type
Definition: rlm_dhcpv4.c:46
static unlang_action_t mod_process(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
Send packets outbound.
Definition: rlm_dhcpv4.c:131
request_t * request
Definition: rlm_dhcpv4.c:102
static fr_dict_attr_t const * attr_net_dst_port
Definition: rlm_dhcpv4.c:48
static fr_dict_t const * dict_freeradius
Definition: rlm_dhcpv4.c:35
static fr_dict_attr_t const * attr_gateway_ip_address
Definition: rlm_dhcpv4.c:49
static fr_dict_t const * dict_dhcpv4
Definition: rlm_dhcpv4.c:34
uint8_t * buffer
for encoding packets
Definition: rlm_dhcpv4.c:78
uint32_t buffer_size
Maximum packet size.
Definition: rlm_dhcpv4.c:79
static unlang_action_t dhcpv4_resume(rlm_rcode_t *p_result, module_ctx_t const *mctx, UNUSED request_t *request)
Definition: rlm_dhcpv4.c:115
fr_dict_autoload_t rlm_dhcpv4_dict[]
Definition: rlm_dhcpv4.c:38
static int mod_thread_instantiate(module_thread_inst_ctx_t const *mctx)
Instantiate thread data for the submodule.
Definition: rlm_dhcpv4.c:258
fr_udp_queue_config_t config
UDP queue config.
Definition: rlm_dhcpv4.c:71
static fr_dict_attr_t const * attr_net_dst_ip
Definition: rlm_dhcpv4.c:47
static fr_dict_attr_t const * attr_transaction_id
Definition: rlm_dhcpv4.c:44
static const conf_parser_t module_config[]
Definition: rlm_dhcpv4.c:83
fr_udp_queue_t * uq
udp queue handler
Definition: rlm_dhcpv4.c:77
static int mod_instantiate(module_inst_ctx_t const *mctx)
Bootstrap the module.
Definition: rlm_dhcpv4.c:290
static fr_dict_attr_t const * attr_message_type
Definition: rlm_dhcpv4.c:45
static int instantiate(module_inst_ctx_t const *mctx)
Definition: rlm_rest.c:1302
#define SECTION_NAME(_name1, _name2)
Define a section name consisting of a verb and a noun.
Definition: section.h:40
CONF_SECTION * conf
Module's instance configuration.
Definition: module.h:329
void * data
Module's instance data.
Definition: module.h:271
#define MODULE_BINDING_TERMINATOR
Terminate a module binding list.
Definition: module.h:151
Named methods exported by a module.
Definition: module.h:173
unlang_action_t unlang_module_yield(request_t *request, module_method_t resume, unlang_module_signal_t signal, fr_signal_t sigmask, void *rctx)
Yield a request back to the interpreter from within a module.
Definition: module.c:419
RETURN_MODULE_FAIL
eap_aka_sim_process_conf_t * inst
fr_pair_t * vp
Stores an attribute, a value and various bits of other data.
Definition: pair.h:68
int fr_udp_queue_write(TALLOC_CTX *ctx, fr_udp_queue_t *uq, uint8_t const *packet, size_t packet_len, fr_ipaddr_t const *ipaddr, int port, void *rctx)
Write packet to socket, OR enqueue it if we get EAGAIN.
Definition: udp_queue.c:260
fr_udp_queue_t * fr_udp_queue_alloc(TALLOC_CTX *ctx, fr_udp_queue_config_t const *config, fr_event_list_t *el, fr_udp_queue_resume_t resume)
Allocate an outbound UDP queue.
Definition: udp_queue.c:95
#define FR_PROTO_HEX_DUMP(_data, _data_len, _fmt,...)
Definition: proto.h:41
char const * fr_strerror(void)
Get the last library error.
Definition: strerror.c:554
int nonnull(2, 5))