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: 92faf51c6537a5b545d98206fcf422ba7a1547c3 $
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: 92faf51c6537a5b545d98206fcf422ba7a1547c3 $")
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 */
130static unlang_action_t CC_HINT(nonnull) mod_process(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
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;
139 int code, port, rcode;
140
142
143 /*
144 * We can only send relayed packets, which have a gateway IP
145 */
146 vp = fr_pair_find_by_da(&request->request_pairs, NULL, attr_gateway_ip_address);
147 if (!vp) {
148 REDEBUG("Relayed packets MUST have a Gateway-IP-Address attribute");
150 }
151
152 /*
153 * Get the transaction ID.
154 */
155 vp = fr_pair_find_by_da(&request->request_pairs, NULL, attr_transaction_id);
156 if (vp) {
157 xid = vp->vp_uint32;
158
159 } else if (original) {
160 xid = ntohl(original->xid);
161
162 } else {
163 xid = t->xid++;
164 }
165
166 /*
167 * Set the packet type.
168 *
169 * @todo - make sure it's a client type.
170 */
171 vp = fr_pair_find_by_da(&request->request_pairs, NULL, attr_packet_type);
172 if (vp) {
173 code = vp->vp_uint32;
174
175 } else if ((vp = fr_pair_find_by_da(&request->request_pairs, NULL, attr_message_type)) != NULL) {
176 code = vp->vp_uint8;
177
178 } else {
179 code = request->packet->code;
180 }
181
182 /*
183 * Set the destination port, defaulting to 67
184 */
185 vp = fr_pair_find_by_da_nested(&request->control_pairs, NULL, attr_net_dst_port);
186 if (vp) {
187 port = vp->vp_uint16;
188 } else {
189 port = 67; /* DHCPv4 server port */
190 }
191
192 /*
193 * Get the destination address / port, and unicast it there.
194 */
195 vp = fr_pair_find_by_da_nested(&request->control_pairs, NULL, attr_net_dst_ip);
196 if (!vp || (vp->vp_ip.af != AF_INET)) {
197 RDEBUG("No control.Net.Dst.IP, cannot relay packet");
199 }
200
201 /*
202 * Encode the packet using the original information.
203 */
204 data_len = fr_dhcpv4_encode(t->buffer, t->buffer_size, original, code, xid, &request->request_pairs);
205 if (data_len <= 0) {
206 RPEDEBUG("Failed encoding DHCPV4 request");
208 }
209
210 /*
211 * Enforce some other RFC requirements.
212 */
213 packet = (dhcp_packet_t *) t->buffer;
214 if (packet->opcode == 1) {
215 if (original) {
216 if (original->hops < 255) packet->hops = original->hops + 1;
217 } else {
218 if (packet->hops < 255) packet->hops++;
219 }
220
221 } /* else sending a server message? OK boomer. */
222
223 FR_PROTO_HEX_DUMP(t->buffer, data_len, "DHCPv4");
224
225 d = talloc_zero(request, rlm_dhcpv4_delay_t);
226 if (!d) RETURN_UNLANG_FAIL;
227
228 *d = (rlm_dhcpv4_delay_t) {
229 .request = request,
230 .sent = false,
231 };
232
233 if (RDEBUG_ENABLED) {
234 RDEBUG("Sending %s XID %08X to %pV:%d", dhcp_message_types[code], request->packet->id, &vp->data, port);
235 RINDENT();
236 log_request_pair_list(L_DBG_LVL_1, request, NULL, &request->request_pairs, NULL);
237 REXDENT();
238 }
239
240 rcode = fr_udp_queue_write(d, t->uq, t->buffer, data_len, &vp->vp_ip, port, d);
241 if (rcode > 0) {
242 talloc_free(d);
244 }
245 if (rcode < 0) {
246 talloc_free(d);
248 }
249
250 return unlang_module_yield(request, dhcpv4_resume, NULL, 0, d);
251}
252
253
254/** Instantiate thread data for the submodule.
255 *
256 */
258{
259 rlm_dhcpv4_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_dhcpv4_t);
260 rlm_dhcpv4_thread_t *t = talloc_get_type_abort(mctx->thread, rlm_dhcpv4_thread_t);
261 CONF_SECTION *conf = mctx->mi->conf;
262
263 t->buffer = talloc_array(t, uint8_t, inst->max_packet_size);
264 if (!t->buffer) {
265 cf_log_err(conf, "Failed allocating buffer");
266 return -1;
267 }
268
269 t->buffer_size = inst->max_packet_size;
270
271 t->uq = fr_udp_queue_alloc(t, &inst->config, mctx->el, dhcpv4_queue_resume);
272 if (!t->uq) {
273 cf_log_err(conf, "Failed allocating outbound udp queue - %s", fr_strerror());
274 return -1;
275 }
276
277 return 0;
278}
279
280/** Bootstrap the module
281 *
282 * Bootstrap I/O and type submodules.
283 *
284 * @param[in] mctx data for this module
285 * @return
286 * - 0 on success.
287 * - -1 on failure.
288 */
289static int mod_instantiate(module_inst_ctx_t const *mctx)
290{
291 rlm_dhcpv4_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_dhcpv4_t);
292 CONF_SECTION *conf = mctx->mi->conf;
293
294 /*
295 * Ensure that we have a destination address.
296 */
297 if (inst->config.ipaddr.af == AF_UNSPEC) {
298 cf_log_err(conf, "A value must be given for 'ipaddr'");
299 return -1;
300 }
301
302 if (inst->config.ipaddr.af != AF_INET) {
303 cf_log_err(conf, "DHCPv4 can only use IPv4 addresses in 'ipaddr'");
304 return -1;
305 }
306
307 if (!inst->config.port) {
308 cf_log_err(conf, "A value must be given for 'port'");
309 return -1;
310 }
311
312 /*
313 * Clamp max_packet_size
314 */
315 FR_INTEGER_BOUND_CHECK("max_packet_size", inst->max_packet_size, >=, DEFAULT_PACKET_SIZE);
316 FR_INTEGER_BOUND_CHECK("max_packet_size", inst->max_packet_size, <=, MAX_PACKET_SIZE);
317
318 return 0;
319}
320
323 .common = {
324 .magic = MODULE_MAGIC_INIT,
325 .name = "dhcpv4",
326 .inst_size = sizeof(rlm_dhcpv4_t),
327 .instantiate = mod_instantiate,
328
330
331 .thread_inst_size = sizeof(rlm_dhcpv4_thread_t),
332 .thread_inst_type = "rlm_dhcpv4_thread_t",
333 .thread_instantiate = mod_thread_instantiate
334 },
335 .method_group = {
336 .bindings = (module_method_binding_t[]){
337 { .section = SECTION_NAME(CF_IDENT_ANY, CF_IDENT_ANY), .method = mod_process },
339 }
340 }
341};
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:506
#define UNUSED
Definition build.h:336
#define CONF_PARSER_TERMINATOR
Definition cf_parse.h:657
#define FR_INTEGER_BOUND_CHECK(_name, _var, _op, _bound)
Definition cf_parse.h:517
#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:594
A section grouping multiple CONF_PAIR.
Definition cf_priv.h:101
#define cf_log_err(_cf, _fmt,...)
Definition cf_util.h:285
#define CF_IDENT_ANY
Definition cf_util.h:75
#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:292
fr_dict_t const ** out
Where to write a pointer to the loaded/resolved fr_dict_t.
Definition dict.h:305
#define DICT_AUTOLOAD_TERMINATOR
Definition dict.h:311
Specifies an attribute which must be present for the module to function.
Definition dict.h:291
Specifies a dictionary which must be loaded/loadable for the module to function.
Definition dict.h:304
#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:1636
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:455
#define RPEDEBUG(fmt,...)
Definition log.h:388
#define RINDENT()
Indent R* messages by one level.
Definition log.h:442
@ 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_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:784
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:163
char const * dhcp_message_types[]
Definition base.c:129
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:353
#define REDEBUG(fmt,...)
#define RDEBUG(fmt,...)
#define RDEBUG_ENABLED()
static rs_t * conf
Definition radsniff.c:52
#define RETURN_UNLANG_FAIL
Definition rcode.h:63
#define RETURN_UNLANG_OK
Definition rcode.h:64
#define RETURN_UNLANG_NOOP
Definition rcode.h:69
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:322
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:257
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:289
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:351
size_t inst_size
Size of the module's instance data.
Definition module.h:212
void * data
Module's instance data.
Definition module.h:293
#define MODULE_BINDING_TERMINATOR
Terminate a module binding list.
Definition module.h:152
Named methods exported by a module.
Definition module.h:174
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_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:553
int nonnull(2, 5))