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: 3be14b864946e15203c61e19d318cd8ad323c9a2 $
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: 3be14b864946e15203c61e19d318cd8ad323c9a2 $")
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 #include <ctype.h>
35 
36 static fr_dict_t const *dict_dhcpv4;
37 static fr_dict_t const *dict_freeradius;
38 
41  { .out = &dict_dhcpv4, .proto = "dhcpv4" },
42  { .out = &dict_freeradius, .proto = "freeradius" },
43  { NULL }
44 };
45 
52 
55  { .out = &attr_transaction_id, .name = "Transaction-Id", .type = FR_TYPE_UINT32, .dict = &dict_dhcpv4 },
56  { .out = &attr_gateway_ip_address, .name = "Gateway-IP-Address", .type = FR_TYPE_IPV4_ADDR, .dict = &dict_dhcpv4 },
57  { .out = &attr_message_type, .name = "Message-Type", .type = FR_TYPE_UINT8, .dict = &dict_dhcpv4 },
58  { .out = &attr_packet_type, .name = "Packet-Type", .type = FR_TYPE_UINT32, .dict = &dict_dhcpv4 },
59  { .out = &attr_net_dst_ip, .name = "Net.Dst.IP", .type = FR_TYPE_COMBO_IP_ADDR, .dict = &dict_freeradius },
60  { .out = &attr_net_dst_port, .name = "Net.Dst.Port", .type = FR_TYPE_UINT16, .dict = &dict_freeradius },
61  { NULL }
62 };
63 
64 
65 /*
66  * Define a structure for our module configuration.
67  *
68  * These variables do not need to be in a structure, but it's
69  * a lot cleaner to do so, and a pointer to the structure can
70  * be used as the instance handle.
71  */
72 typedef struct {
73  fr_udp_queue_config_t config; //!< UDP queue config
74 
75  uint32_t max_packet_size; //!< Maximum packet size.
76 } rlm_dhcpv4_t;
77 
78 typedef struct {
79  fr_udp_queue_t *uq; //!< udp queue handler
80  uint8_t *buffer; //!< for encoding packets
81  uint32_t buffer_size; //!< Maximum packet size.
82  uint32_t xid; //!< XID
84 
85 static const conf_parser_t module_config[] = {
88 
89  { FR_CONF_OFFSET("port", rlm_dhcpv4_t, config.port), .dflt = "68" },
90 
91  { FR_CONF_OFFSET("interface", rlm_dhcpv4_t, config.interface) },
92 
93  { FR_CONF_OFFSET_IS_SET("send_buff", FR_TYPE_UINT32, 0, rlm_dhcpv4_t, config.send_buff) },
94 
95  { FR_CONF_OFFSET("max_packet_size", rlm_dhcpv4_t, max_packet_size), .dflt = "576" },
96  { FR_CONF_OFFSET("max_queued_packets", rlm_dhcpv4_t, config.max_queued_packets), .dflt = "65536" },
97 
98  { FR_CONF_OFFSET("timeout", rlm_dhcpv4_t, config.max_queued_time), .dflt = "0" },
99 
101 };
102 
103 /** Bootstrap the module
104  *
105  * Bootstrap I/O and type submodules.
106  *
107  * @param[in] mctx data for this module
108  * @return
109  * - 0 on success.
110  * - -1 on failure.
111  */
112 static int mod_bootstrap(module_inst_ctx_t const *mctx)
113 {
114  rlm_dhcpv4_t *inst = talloc_get_type_abort(mctx->inst->data, rlm_dhcpv4_t);
115  CONF_SECTION *conf = mctx->inst->conf;
116 
117  /*
118  * Ensure that we have a destination address.
119  */
120  if (inst->config.ipaddr.af == AF_UNSPEC) {
121  cf_log_err(conf, "A value must be given for 'ipaddr'");
122  return -1;
123  }
124 
125  if (inst->config.ipaddr.af != AF_INET) {
126  cf_log_err(conf, "DHCPv4 can only use IPv4 addresses in 'ipaddr'");
127  return -1;
128  }
129 
130  if (!inst->config.port) {
131  cf_log_err(conf, "A value must be given for 'port'");
132  return -1;
133  }
134 
135  /*
136  * Clamp max_packet_size
137  */
138  FR_INTEGER_BOUND_CHECK("max_packet_size", inst->max_packet_size, >=, DEFAULT_PACKET_SIZE);
139  FR_INTEGER_BOUND_CHECK("max_packet_size", inst->max_packet_size, <=, MAX_PACKET_SIZE);
140 
141  return 0;
142 }
143 
144 typedef struct {
146  bool sent;
148 
149 static void dhcpv4_queue_resume(bool sent, void *rctx)
150 {
151  rlm_dhcpv4_delay_t *d = talloc_get_type_abort(rctx, rlm_dhcpv4_delay_t);
152 
153  d->sent = sent;
154 
156 }
157 
158 /** Instantiate thread data for the submodule.
159  *
160  */
162 {
163  rlm_dhcpv4_t *inst = talloc_get_type_abort(mctx->inst->data, rlm_dhcpv4_t);
164  rlm_dhcpv4_thread_t *t = talloc_get_type_abort(mctx->thread, rlm_dhcpv4_thread_t);
165  CONF_SECTION *conf = mctx->inst->conf;
166 
167  t->buffer = talloc_array(t, uint8_t, inst->max_packet_size);
168  if (!t->buffer) {
169  cf_log_err(conf, "Failed allocating buffer");
170  return -1;
171  }
172 
173  t->buffer_size = inst->max_packet_size;
174 
175  t->uq = fr_udp_queue_alloc(t, &inst->config, mctx->el, dhcpv4_queue_resume);
176  if (!t->uq) {
177  cf_log_err(conf, "Failed allocating outbound udp queue - %s", fr_strerror());
178  return -1;
179  }
180 
181  return 0;
182 }
183 
184 static unlang_action_t dhcpv4_resume(rlm_rcode_t *p_result, module_ctx_t const *mctx, UNUSED request_t *request)
185 {
186  rlm_dhcpv4_delay_t *d = talloc_get_type_abort(mctx->rctx, rlm_dhcpv4_delay_t);
187 
188  if (!d->sent) {
189  talloc_free(d);
191  }
192 
193  talloc_free(d);
195 }
196 
197 
198 /** Send packets outbound.
199  *
200  */
201 static unlang_action_t CC_HINT(nonnull) mod_process(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
202 {
203  rlm_dhcpv4_thread_t *t = talloc_get_type_abort(mctx->thread, rlm_dhcpv4_thread_t);
204  ssize_t data_len;
205  dhcp_packet_t *original = (dhcp_packet_t *) request->packet->data;
206  dhcp_packet_t *packet;
207 
208  uint32_t xid;
209  fr_pair_t *vp;
210  int code, port, rcode;
211 
213 
214  /*
215  * We can only send relayed packets, which have a gateway IP
216  */
217  vp = fr_pair_find_by_da(&request->request_pairs, NULL, attr_gateway_ip_address);
218  if (!vp) {
219  REDEBUG("Relayed packets MUST have a Gateway-IP-Address attribute");
221  }
222 
223  /*
224  * Get the transaction ID.
225  */
226  vp = fr_pair_find_by_da(&request->request_pairs, NULL, attr_transaction_id);
227  if (vp) {
228  xid = vp->vp_uint32;
229 
230  } else if (original) {
231  xid = ntohl(original->xid);
232 
233  } else {
234  xid = t->xid++;
235  }
236 
237  /*
238  * Set the packet type.
239  *
240  * @todo - make sure it's a client type.
241  */
242  vp = fr_pair_find_by_da(&request->request_pairs, NULL, attr_packet_type);
243  if (vp) {
244  code = vp->vp_uint32;
245 
246  } else if ((vp = fr_pair_find_by_da(&request->request_pairs, NULL, attr_message_type)) != NULL) {
247  code = vp->vp_uint8;
248 
249  } else {
250  code = request->packet->code;
251  }
252 
253  /*
254  * Set the destination port, defaulting to 67
255  */
256  vp = fr_pair_find_by_da_nested(&request->control_pairs, NULL, attr_net_dst_port);
257  if (vp) {
258  port = vp->vp_uint16;
259  } else {
260  port = 67; /* DHCPv4 server port */
261  }
262 
263  /*
264  * Get the destination address / port, and unicast it there.
265  */
266  vp = fr_pair_find_by_da_nested(&request->control_pairs, NULL, attr_net_dst_ip);
267  if (!vp || (vp->vp_ip.af != AF_INET)) {
268  RDEBUG("No &control.Net.Dst.IP, cannot relay packet");
270  }
271 
272  /*
273  * Encode the packet using the original information.
274  */
275  data_len = fr_dhcpv4_encode(t->buffer, t->buffer_size, original, code, xid, &request->request_pairs);
276  if (data_len <= 0) {
277  RPEDEBUG("Failed encoding DHCPV4 request");
279  }
280 
281  /*
282  * Enforce some other RFC requirements.
283  */
284  packet = (dhcp_packet_t *) t->buffer;
285  if (packet->opcode == 1) {
286  if (original) {
287  if (original->hops < 255) packet->hops = original->hops + 1;
288  } else {
289  if (packet->hops < 255) packet->hops++;
290  }
291 
292  } /* else sending a server message? OK boomer. */
293 
294  FR_PROTO_HEX_DUMP(t->buffer, data_len, "DHCPv4");
295 
296  d = talloc_zero(request, rlm_dhcpv4_delay_t);
297  if (!d) RETURN_MODULE_FAIL;
298 
299  *d = (rlm_dhcpv4_delay_t) {
300  .request = request,
301  .sent = false,
302  };
303 
304  if (RDEBUG_ENABLED) {
305  RDEBUG("Sending %s XID %08X to %pV:%d", dhcp_message_types[code], request->packet->id, &vp->data, port);
306  RINDENT();
307  log_request_pair_list(L_DBG_LVL_1, request, NULL, &request->request_pairs, NULL);
308  REXDENT();
309  }
310 
311  rcode = fr_udp_queue_write(d, t->uq, t->buffer, data_len, &vp->vp_ip, port, d);
312  if (rcode > 0) {
313  talloc_free(d);
315  }
316  if (rcode < 0) {
317  talloc_free(d);
319  }
320 
321  return unlang_module_yield(request, dhcpv4_resume, NULL, 0, d);
322 }
323 
324 extern module_rlm_t rlm_dhcpv4;
326  .common = {
327  .magic = MODULE_MAGIC_INIT,
328  .name = "dhcpv4",
329  .inst_size = sizeof(rlm_dhcpv4_t),
330  .bootstrap = mod_bootstrap,
331 
333 
334  .thread_inst_size = sizeof(rlm_dhcpv4_thread_t),
335  .thread_inst_type = "rlm_dhcpv4_thread_t",
336  .thread_instantiate = mod_thread_instantiate
337  },
338  .method_names = (module_method_name_t[]){
339  { .name1 = CF_IDENT_ANY, .name2 = CF_IDENT_ANY, .method = mod_process },
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:444
#define UNUSED
Definition: build.h:313
#define CONF_PARSER_TERMINATOR
Definition: cf_parse.h:626
#define FR_INTEGER_BOUND_CHECK(_name, _var, _op, _bound)
Definition: cf_parse.h:486
#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:563
A section grouping multiple CONF_PAIR.
Definition: cf_priv.h:89
#define cf_log_err(_cf, _fmt,...)
Definition: cf_util.h:265
#define CF_IDENT_ANY
Definition: cf_util.h:78
#define DEFAULT_PACKET_SIZE
Definition: dhcpv4.h:105
#define MAX_PACKET_SIZE
Definition: dhcpv4.h:106
uint8_t hops
Definition: dhcpv4.h:86
uint32_t xid
Definition: dhcpv4.h:87
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
CONF_SECTION *_CONST conf
Module's instance configuration.
Definition: dl_module.h:166
void unlang_interpret_mark_runnable(request_t *request)
Mark a request as resumable.
Definition: interpret.c:1340
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
#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:63
dl_module_inst_t const * inst
Dynamic loader API handle for the module.
Definition: module_ctx.h:52
void * thread
Thread instance data.
Definition: module_ctx.h:62
dl_module_inst_t const * inst
Dynamic loader API handle for the module.
Definition: module_ctx.h:59
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:51
Temporary structure to hold arguments for thread_instantiation calls.
Definition: module_ctx.h:58
Specifies a module method identifier.
Definition: module_method.c:36
module_t common
Common fields presented by all modules.
Definition: module_rlm.h:37
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
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:765
static const conf_parser_t config[]
Definition: base.c:188
char const * dhcp_message_types[]
Definition: base.c:124
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:311
#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:149
uint32_t max_packet_size
Maximum packet size.
Definition: rlm_dhcpv4.c:75
module_rlm_t rlm_dhcpv4
Definition: rlm_dhcpv4.c:325
uint32_t xid
XID.
Definition: rlm_dhcpv4.c:82
fr_dict_attr_autoload_t rlm_dhcpv4_dict_attr[]
Definition: rlm_dhcpv4.c:54
static fr_dict_attr_t const * attr_packet_type
Definition: rlm_dhcpv4.c:48
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:201
request_t * request
Definition: rlm_dhcpv4.c:145
static fr_dict_attr_t const * attr_net_dst_port
Definition: rlm_dhcpv4.c:50
static fr_dict_t const * dict_freeradius
Definition: rlm_dhcpv4.c:37
static int mod_bootstrap(module_inst_ctx_t const *mctx)
Bootstrap the module.
Definition: rlm_dhcpv4.c:112
static fr_dict_attr_t const * attr_gateway_ip_address
Definition: rlm_dhcpv4.c:51
static fr_dict_t const * dict_dhcpv4
Definition: rlm_dhcpv4.c:36
uint8_t * buffer
for encoding packets
Definition: rlm_dhcpv4.c:80
uint32_t buffer_size
Maximum packet size.
Definition: rlm_dhcpv4.c:81
static unlang_action_t dhcpv4_resume(rlm_rcode_t *p_result, module_ctx_t const *mctx, UNUSED request_t *request)
Definition: rlm_dhcpv4.c:184
fr_dict_autoload_t rlm_dhcpv4_dict[]
Definition: rlm_dhcpv4.c:40
static int mod_thread_instantiate(module_thread_inst_ctx_t const *mctx)
Instantiate thread data for the submodule.
Definition: rlm_dhcpv4.c:161
fr_udp_queue_config_t config
UDP queue config.
Definition: rlm_dhcpv4.c:73
static fr_dict_attr_t const * attr_net_dst_ip
Definition: rlm_dhcpv4.c:49
static fr_dict_attr_t const * attr_transaction_id
Definition: rlm_dhcpv4.c:46
static const conf_parser_t module_config[]
Definition: rlm_dhcpv4.c:85
fr_udp_queue_t * uq
udp queue handler
Definition: rlm_dhcpv4.c:79
static fr_dict_attr_t const * attr_message_type
Definition: rlm_dhcpv4.c:47
#define MODULE_NAME_TERMINATOR
Definition: module.h:135
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:575
RETURN_MODULE_FAIL
if(!subtype_vp) goto 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))