The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
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 */
24RCSID("$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
34static fr_dict_t const *dict_dhcpv4;
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 */
70typedef struct {
71 fr_udp_queue_config_t config; //!< UDP queue config
72
73 uint32_t max_packet_size; //!< Maximum packet size.
75
76typedef 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
83static 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
101typedef struct {
103 bool sent;
105
106static 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
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 */
131static 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 */
290static 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
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:483
#define UNUSED
Definition build.h:315
#define CONF_PARSER_TERMINATOR
Definition cf_parse.h:642
#define FR_INTEGER_BOUND_CHECK(_name, _var, _op, _bound)
Definition cf_parse.h:502
#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:579
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 opcode
Definition dhcpv4.h:67
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:268
fr_dict_t const ** out
Where to write a pointer to the loaded/resolved fr_dict_t.
Definition dict.h:281
Specifies an attribute which must be present for the module to function.
Definition dict.h:267
Specifies a dictionary which must be loaded/loadable for the module to function.
Definition dict.h:280
#define MODULE_MAGIC_INIT
Stop people using different module/library/server versions together.
Definition dl_module.h:63
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.
@ FR_TYPE_UINT16
16 Bit unsigned integer.
@ FR_TYPE_UINT8
8 Bit unsigned integer.
@ FR_TYPE_UINT32
32 Bit unsigned integer.
@ FR_TYPE_COMBO_IP_ADDR
IPv4 or IPv6 address depending on length.
unsigned int uint32_t
long int ssize_t
unsigned char uint8_t
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_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
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
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
#define RETURN_MODULE_FAIL
Definition rcode.h:56
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:1310
#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
size_t inst_size
Size of the module's instance data.
Definition module.h:203
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
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))