The FreeRADIUS server $Id: a4e7f8c94e8038b7627bcf52356b7ed15955c1d7 $
Loading...
Searching...
No Matches
rlm_dhcpv4.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 (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: 89f3de6d32eb7d4d3706a4fc60ae99477d96cdee $
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: 89f3de6d32eb7d4d3706a4fc60ae99477d96cdee $")
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
33static fr_dict_t const *dict_dhcpv4;
35
38 { .out = &dict_dhcpv4, .proto = "dhcpv4" },
39 { .out = &dict_freeradius, .proto = "freeradius" },
41};
42
49
52 { .out = &attr_transaction_id, .name = "Transaction-Id", .type = FR_TYPE_UINT32, .dict = &dict_dhcpv4 },
53 { .out = &attr_gateway_ip_address, .name = "Gateway-IP-Address", .type = FR_TYPE_IPV4_ADDR, .dict = &dict_dhcpv4 },
54 { .out = &attr_message_type, .name = "Message-Type", .type = FR_TYPE_UINT8, .dict = &dict_dhcpv4 },
55 { .out = &attr_packet_type, .name = "Packet-Type", .type = FR_TYPE_UINT32, .dict = &dict_dhcpv4 },
56 { .out = &attr_net_dst_ip, .name = "Net.Dst.IP", .type = FR_TYPE_COMBO_IP_ADDR, .dict = &dict_freeradius },
57 { .out = &attr_net_dst_port, .name = "Net.Dst.Port", .type = FR_TYPE_UINT16, .dict = &dict_freeradius },
59};
60
61
62/*
63 * Define a structure for our module configuration.
64 *
65 * These variables do not need to be in a structure, but it's
66 * a lot cleaner to do so, and a pointer to the structure can
67 * be used as the instance handle.
68 */
69typedef struct {
70 fr_udp_queue_config_t config; //!< UDP queue config
71
72 uint32_t max_packet_size; //!< Maximum packet size.
74
75typedef struct {
76 fr_udp_queue_t *uq; //!< udp queue handler
77 uint8_t *buffer; //!< for encoding packets
78 uint32_t buffer_size; //!< Maximum packet size.
79 uint32_t xid; //!< XID
81
82static const conf_parser_t module_config[] = {
85
86 { FR_CONF_OFFSET("port", rlm_dhcpv4_t, config.port), .dflt = "68" },
87
88 { FR_CONF_OFFSET("interface", rlm_dhcpv4_t, config.interface) },
89
90 { FR_CONF_OFFSET_IS_SET("send_buff", FR_TYPE_UINT32, 0, rlm_dhcpv4_t, config.send_buff) },
91
92 { FR_CONF_OFFSET("max_packet_size", rlm_dhcpv4_t, max_packet_size), .dflt = "576" },
93 { FR_CONF_OFFSET("max_queued_packets", rlm_dhcpv4_t, config.max_queued_packets), .dflt = "65536" },
94
95 { FR_CONF_OFFSET("timeout", rlm_dhcpv4_t, config.max_queued_time), .dflt = "0" },
96
98};
99
100typedef struct {
102 bool sent;
104
105static void dhcpv4_queue_resume(bool sent, void *rctx)
106{
107 rlm_dhcpv4_delay_t *d = talloc_get_type_abort(rctx, rlm_dhcpv4_delay_t);
108
109 d->sent = sent;
110
112}
113
115{
116 rlm_dhcpv4_delay_t *d = talloc_get_type_abort(mctx->rctx, rlm_dhcpv4_delay_t);
117
118 if (!d->sent) {
119 talloc_free(d);
121 }
122
123 talloc_free(d);
125}
126
127/** Send packets outbound.
128 *
129 */
131{
132 rlm_dhcpv4_thread_t *t = talloc_get_type_abort(mctx->thread, rlm_dhcpv4_thread_t);
133 ssize_t data_len;
134 dhcp_packet_t *original = (dhcp_packet_t *) request->packet->data;
135 dhcp_packet_t *packet;
136
137 uint32_t xid;
138 fr_pair_t *vp;
141 int port, rcode;
142
144
145 /*
146 * We can only send relayed packets, which have a gateway IP
147 */
148 vp = fr_pair_find_by_da(&request->request_pairs, NULL, attr_gateway_ip_address);
149 if (!vp) {
150 REDEBUG("Relayed packets MUST have a Gateway-IP-Address attribute");
152 }
153
154 /*
155 * Get the transaction ID.
156 */
157 vp = fr_pair_find_by_da(&request->request_pairs, NULL, attr_transaction_id);
158 if (vp) {
159 xid = vp->vp_uint32;
160
161 } else if (original) {
162 xid = ntohl(original->xid);
163
164 } else {
165 xid = t->xid++;
166 }
167
168 /*
169 * Set the packet type.
170 *
171 * @todo - make sure it's a client type.
172 */
173 vp = fr_pair_find_by_da(&request->request_pairs, NULL, attr_packet_type);
174 if (vp) {
175 type = vp->vp_uint32;
176
177 } else if ((vp = fr_pair_find_by_da(&request->request_pairs, NULL, attr_message_type)) != NULL) {
178 type = vp->vp_uint8;
179
180 } else {
181 type = request->packet->code;
182 }
183
185 REDEBUG("Invalid DHCP packet type %u", type);
187 }
189
190 /*
191 * Set the destination port, defaulting to 67
192 */
194 if (vp) {
195 port = vp->vp_uint16;
196 } else {
197 port = 67; /* DHCPv4 server port */
198 }
199
200 /*
201 * Get the destination address / port, and unicast it there.
202 */
204 if (!vp || (vp->vp_ip.af != AF_INET)) {
205 RDEBUG("No control.Net.Dst.IP, cannot relay packet");
207 }
208
209 /*
210 * Encode the packet using the original information.
211 */
212 data_len = fr_dhcpv4_encode(t->buffer, t->buffer_size, original, code, xid, &request->request_pairs);
213 if (data_len <= 0) {
214 RPEDEBUG("Failed encoding DHCPV4 request");
216 }
217
218 /*
219 * Enforce some other RFC requirements.
220 */
221 packet = (dhcp_packet_t *) t->buffer;
222 if (packet->opcode == 1) {
223 if (original) {
224 if (original->hops < 255) packet->hops = original->hops + 1;
225 } else {
226 if (packet->hops < 255) packet->hops++;
227 }
228
229 } /* else sending a server message? OK boomer. */
230
231 FR_PROTO_HEX_DUMP(t->buffer, data_len, "DHCPv4");
232
233 d = talloc_zero(request, rlm_dhcpv4_delay_t);
234 if (!d) RETURN_UNLANG_FAIL;
235
236 *d = (rlm_dhcpv4_delay_t) {
237 .request = request,
238 .sent = false,
239 };
240
241 if (RDEBUG_ENABLED) {
242 RDEBUG("Sending %s XID %08" PRIX32 " to %pV:%d", dhcp_message_types[code], (uint32_t)request->packet->id, &vp->data, port);
243 RINDENT();
244 log_request_pair_list(L_DBG_LVL_1, request, NULL, &request->request_pairs, NULL);
245 REXDENT();
246 }
247
248 rcode = fr_udp_queue_write(d, t->uq, t->buffer, data_len, &vp->vp_ip, port, d);
249 if (rcode > 0) {
250 talloc_free(d);
252 }
253 if (rcode < 0) {
254 talloc_free(d);
256 }
257
259}
260
261
262/** Instantiate thread data for the submodule.
263 *
264 */
266{
267 rlm_dhcpv4_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_dhcpv4_t);
268 rlm_dhcpv4_thread_t *t = talloc_get_type_abort(mctx->thread, rlm_dhcpv4_thread_t);
269 CONF_SECTION *conf = mctx->mi->conf;
270
271 t->buffer = talloc_array(t, uint8_t, inst->max_packet_size);
272 if (!t->buffer) {
273 cf_log_err(conf, "Failed allocating buffer");
274 return -1;
275 }
276
277 t->buffer_size = inst->max_packet_size;
278
279 t->uq = fr_udp_queue_alloc(t, &inst->config, mctx->el, dhcpv4_queue_resume);
280 if (!t->uq) {
281 cf_log_err(conf, "Failed allocating outbound udp queue - %s", fr_strerror());
282 return -1;
283 }
284
285 return 0;
286}
287
288/** Bootstrap the module
289 *
290 * Bootstrap I/O and type submodules.
291 *
292 * @param[in] mctx data for this module
293 * @return
294 * - 0 on success.
295 * - -1 on failure.
296 */
297static int mod_instantiate(module_inst_ctx_t const *mctx)
298{
299 rlm_dhcpv4_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_dhcpv4_t);
300 CONF_SECTION *conf = mctx->mi->conf;
301
302 /*
303 * Ensure that we have a destination address.
304 */
305 if (inst->config.ipaddr.af == AF_UNSPEC) {
306 cf_log_err(conf, "A value must be given for 'ipaddr'");
307 return -1;
308 }
309
310 if (inst->config.ipaddr.af != AF_INET) {
311 cf_log_err(conf, "DHCPv4 can only use IPv4 addresses in 'ipaddr'");
312 return -1;
313 }
314
315 if (!inst->config.port) {
316 cf_log_err(conf, "A value must be given for 'port'");
317 return -1;
318 }
319
320 /*
321 * Clamp max_packet_size
322 */
323 FR_INTEGER_BOUND_CHECK("max_packet_size", inst->max_packet_size, >=, DEFAULT_PACKET_SIZE);
324 FR_INTEGER_BOUND_CHECK("max_packet_size", inst->max_packet_size, <=, MAX_PACKET_SIZE);
325
326 return 0;
327}
328
331 .common = {
332 .magic = MODULE_MAGIC_INIT,
333 .name = "dhcpv4",
334 .inst_size = sizeof(rlm_dhcpv4_t),
335 .instantiate = mod_instantiate,
336
338
339 .thread_inst_size = sizeof(rlm_dhcpv4_thread_t),
340 .thread_inst_type = "rlm_dhcpv4_thread_t",
341 .thread_instantiate = mod_thread_instantiate
342 },
343 .method_group = {
344 .bindings = (module_method_binding_t[]){
345 { .section = SECTION_NAME(CF_IDENT_ANY, CF_IDENT_ANY), .method = mod_process },
347 }
348 }
349};
unlang_action_t
Returned by unlang_op_t calls, determine the next action of the interpreter.
Definition action.h:35
static uint8_t request[4]
#define RCSID(id)
Definition build.h:603
#define UNUSED
Definition build.h:415
#define CONF_PARSER_TERMINATOR
Definition cf_parse.h:722
#define FR_INTEGER_BOUND_CHECK(_name, _var, _op, _bound)
Definition cf_parse.h:572
#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:280
#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:294
#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:238
Defines a CONF_PAIR to C data type mapping.
Definition cf_parse.h:649
A section grouping multiple CONF_PAIR.
Definition cf_priv.h:106
#define cf_log_err(_cf, _fmt,...)
Definition cf_util.h:340
#define CF_IDENT_ANY
Definition cf_util.h:80
#define DEFAULT_PACKET_SIZE
Definition dhcpv4.h:89
#define FR_DHCP_PACKET_CODE_VALID(_code)
Definition dhcpv4.h:64
fr_dhcpv4_packet_code_t
Definition dhcpv4.h:43
#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:316
fr_dict_t const ** out
Where to write a pointer to the loaded/resolved fr_dict_t.
Definition dict.h:329
#define DICT_AUTOLOAD_TERMINATOR
Definition dict.h:335
Specifies an attribute which must be present for the module to function.
Definition dict.h:315
Specifies a dictionary which must be loaded/loadable for the module to function.
Definition dict.h:328
#define MODULE_MAGIC_INIT
Stop people using different module/library/server versions together.
Definition dl_module.h:63
talloc_free(hp)
void unlang_interpret_mark_runnable(request_t *request)
Mark a request as resumable.
Definition interpret.c:2017
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:831
#define REXDENT()
Exdent (unindent) R* messages by one level.
Definition log.h:460
#define RPEDEBUG(fmt,...)
Definition log.h:393
#define RINDENT()
Indent R* messages by one level.
Definition log.h:447
@ L_DBG_LVL_1
Highest priority debug messages (-x).
Definition log.h:67
@ 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_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:782
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:707
static const conf_parser_t config[]
Definition base.c:162
char const * dhcp_message_types[FR_DHCP_CODE_MAX]
Definition base.c:129
ssize_t fr_dhcpv4_encode(uint8_t *buffer, size_t buflen, dhcp_packet_t *original, fr_dhcpv4_packet_code_t code, uint32_t xid, fr_pair_list_t *vps)
Definition base.c:358
#define REDEBUG(fmt,...)
#define RDEBUG(fmt,...)
#define RDEBUG_ENABLED()
static rs_t * conf
Definition radsniff.c:52
#define RETURN_UNLANG_FAIL
Definition rcode.h:69
#define RETURN_UNLANG_OK
Definition rcode.h:70
#define RETURN_UNLANG_NOOP
Definition rcode.h:75
static void dhcpv4_queue_resume(bool sent, void *rctx)
Definition rlm_dhcpv4.c:105
uint32_t max_packet_size
Maximum packet size.
Definition rlm_dhcpv4.c:72
module_rlm_t rlm_dhcpv4
Definition rlm_dhcpv4.c:330
uint32_t xid
XID.
Definition rlm_dhcpv4.c:79
fr_dict_attr_autoload_t rlm_dhcpv4_dict_attr[]
Definition rlm_dhcpv4.c:51
static fr_dict_attr_t const * attr_packet_type
Definition rlm_dhcpv4.c:45
request_t * request
Definition rlm_dhcpv4.c:101
static fr_dict_attr_t const * attr_net_dst_port
Definition rlm_dhcpv4.c:47
static fr_dict_t const * dict_freeradius
Definition rlm_dhcpv4.c:34
static unlang_action_t mod_process(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Send packets outbound.
Definition rlm_dhcpv4.c:130
static unlang_action_t dhcpv4_resume(unlang_result_t *p_result, module_ctx_t const *mctx, UNUSED request_t *request)
Definition rlm_dhcpv4.c:114
static fr_dict_attr_t const * attr_gateway_ip_address
Definition rlm_dhcpv4.c:48
static fr_dict_t const * dict_dhcpv4
Definition rlm_dhcpv4.c:33
uint8_t * buffer
for encoding packets
Definition rlm_dhcpv4.c:77
uint32_t buffer_size
Maximum packet size.
Definition rlm_dhcpv4.c:78
fr_dict_autoload_t rlm_dhcpv4_dict[]
Definition rlm_dhcpv4.c:37
static int mod_thread_instantiate(module_thread_inst_ctx_t const *mctx)
Instantiate thread data for the submodule.
Definition rlm_dhcpv4.c:265
fr_udp_queue_config_t config
UDP queue config.
Definition rlm_dhcpv4.c:70
static fr_dict_attr_t const * attr_net_dst_ip
Definition rlm_dhcpv4.c:46
static fr_dict_attr_t const * attr_transaction_id
Definition rlm_dhcpv4.c:43
static const conf_parser_t module_config[]
Definition rlm_dhcpv4.c:82
fr_udp_queue_t * uq
udp queue handler
Definition rlm_dhcpv4.c:76
static int mod_instantiate(module_inst_ctx_t const *mctx)
Bootstrap the module.
Definition rlm_dhcpv4.c:297
static fr_dict_attr_t const * attr_message_type
Definition rlm_dhcpv4.c:44
#define SECTION_NAME(_name1, _name2)
Define a section name consisting of a verb and a noun.
Definition section.h:39
CONF_SECTION * conf
Module's instance configuration.
Definition module.h:355
size_t inst_size
Size of the module's instance data.
Definition module.h:213
void * data
Module's instance data.
Definition module.h:297
#define MODULE_BINDING_TERMINATOR
Terminate a module binding list.
Definition module.h:153
Named methods exported by a module.
Definition module.h:175
@ FR_SIGNAL_NONE
No signals.
Definition signal.h:39
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:431
eap_aka_sim_process_conf_t * inst
fr_aka_sim_id_type_t type
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:42
char const * fr_strerror(void)
Get the last library error.
Definition strerror.c:558
int nonnull(2, 5))