The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
io.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: bc8c2f5e425fadc79767d3409c937329bee576cc $
19 * @file rlm_unbound/io.c
20 * @brief Provides interface between libunbound and the FreeRADIUS event loop
21 *
22 * @copyright 2019 The FreeRADIUS server project
23 * @copyright 2019 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
24 */
25RCSID("$Id: bc8c2f5e425fadc79767d3409c937329bee576cc $")
26
27#define LOG_PREFIX "unbound"
28
29#include <freeradius-devel/server/log.h>
30#include <freeradius-devel/util/debug.h>
31#include <freeradius-devel/util/syserror.h>
32
33#include "io.h"
34
35#ifdef HAVE_WDOCUMENTATION
36DIAG_OFF(documentation)
37#endif
38#include <unbound-event.h>
39#ifdef HAVE_WDOCUMENTATION
40DIAG_ON(documentation)
41#endif
42
43/** Definition for libunbound's event callback
44 *
45 * Here because they don't provide one.
46 */
47typedef void(*unbound_cb_t)(int, short flags, void *uctx);
48
49/** Wrapper around event handle for our event loop
50 *
51 * This stores libunbound specific information for an event in our event loop.
52 *
53 * Lifetime should be bound to the event base.
54 */
55typedef struct {
56 struct ub_event base; //!< Unbound event base, which we populate with
57 ///< callback functions for adding events for FDs
58 ///< setting timers etc...
59 ///< MUST BE LISTED FIRST.
60
61 unbound_io_event_base_t *ev_b; //!< Event base this handle was created for.
62
63 fr_timer_t *timer_ev; //!< Stores the pointer to the enabled timer for
64 ///< this event handled. libunbound uses a single
65 ///< handle for managing related FD events and
66 ///< timers, which is weird, but ok...
67
68 short events; //!< The events this event handle should receive
69 ///< when activated.
70
71 int fd; //!< File descriptor this event handle relates to.
72
73 unbound_cb_t cb; //!< The callback we need to call when a specified
74 ///< event happens.
75
76 void *uctx; //!< This is the argument libunbound wants passed to
77 ///< the callback when it's called. It usually
78 ///< contains libunbound's internal connection handled.
79 ///< We don't have any visibility, it just remains
80 ///< an opaque blob to us.
81
82 bool active; //!< Whether this event is considered active.
84
85/** Alter the enabled flags associated with the event
86 *
87 * Event *MUST* be disabled before these flags are changed.
88 */
89static void _unbound_io_event_flags_add(struct ub_event *ub_ev, short flags)
90{
91 unbound_io_event_t *ev = talloc_get_type_abort(ub_ev, unbound_io_event_t);
92 short new = ev->events | flags;
93
94 DEBUG4("unbound event %p - Adding flags %i (current %i, new %i)", ev, flags, ev->events, new);
95
96 fr_assert(!ev->active); /* must not be active */
97
98 ev->events = new;
99}
100
101/** Alter the enabled flags associated with the event
102 *
103 * Event *MUST* be disabled before these flags are changed.
104 */
105static void _unbound_io_event_flags_del(struct ub_event *ub_ev, short flags)
106{
107 unbound_io_event_t *ev = talloc_get_type_abort(ub_ev, unbound_io_event_t);
108 short new = ev->events & ~flags;
109
110 fr_assert(!ev->active); /* must not be active */
111
112 DEBUG4("unbound event %p - Removing flags %i (current %i, new %i)", ev, flags, ev->events, new);
113
114 ev->events = new;
115}
116
117/** Change the file descriptor associated with an event
118 *
119 * Event *MUST* be disabled before changing the fd.
120 */
121static void _unbound_io_event_fd_set(struct ub_event *ub_ev, int fd)
122{
123 unbound_io_event_t *ev = talloc_get_type_abort(ub_ev, unbound_io_event_t);
124
125 fr_assert(!ev->active); /* must not be active */
126
127 if (fd == ev->fd) return;
128
129 DEBUG4("unbound event %p - Changed FD from %i to %i", ev, ev->fd, fd);
130
131 ev->fd = fd;
132}
133
134/** Free an event, and, by the magic of talloc, any timers or fd events
135 *
136 */
137static void _unbound_io_event_free(struct ub_event *ub_ev)
138{
139 unbound_io_event_t *ev = talloc_get_type_abort(ub_ev, unbound_io_event_t);
140
141 DEBUG4("unbound event %p - Freed", ev);
142
143 talloc_free(ev);
144}
145
146/** Timeout fired
147 *
148 * Unbound uses these timeouts as part of its mechanism to measure rtt from
149 * candidate DNS servers, working out which is the fastest to use for any
150 * given query. The timeout happening causes the timeout against the server
151 * to be increased for any subsequent queries sent to it.
152 */
154{
155 unbound_io_event_t *ev = talloc_get_type_abort(uctx, unbound_io_event_t);
156
157 DEBUG4("unbound event %p - Timeout", ev);
158
159 ev->cb(-1, UB_EV_TIMEOUT, ev->uctx); /* Inform libunbound */
160}
161
162/** Unbound FD became readable
163 *
164 * Because we don't have the separation between the IO event loop
165 * and the event loop processing results, we call ub_process
166 * immediately after calling the IO callback.
167 */
168static void _unbound_io_service_readable(fr_event_list_t *el, int fd, UNUSED int flags, void *uctx)
169{
170 unbound_io_event_t *ev = talloc_get_type_abort(uctx, unbound_io_event_t);
171
172 fr_assert(ev->active); /* must be active */
173
174 DEBUG4("unbound event %p - FD %i now readable", ev, fd);
175
176 ev->cb(fd, UB_EV_READ, ev->uctx); /* Inform libunbound */
177
178 /*
179 * Remove IO events
180 */
181 if (!(ev->events & UB_EV_PERSIST)) {
182 DEBUG4("unbound event %p - UB_EV_PERSIST not set - Removing events for FD %i", ev, ev->fd);
184 PERROR("unbound event %p - De-registration failed for FD %i", ev, ev->fd);
185 }
186 }
187}
188
189/** Unbound FD became writable
190 *
191 */
192static void _unbound_io_service_writable(fr_event_list_t *el, int fd, UNUSED int flags, void *uctx)
193{
194 unbound_io_event_t *ev = talloc_get_type_abort(uctx, unbound_io_event_t);
195
196 fr_assert(ev->active); /* must be active */
197
198 DEBUG4("unbound event %p - FD %i now writable", ev, fd);
199
200 ev->cb(fd, UB_EV_WRITE, ev->uctx); /* Inform libunbound */
201
202 /*
203 * Remove IO events
204 */
205 if (!(ev->events & UB_EV_PERSIST)) {
206 DEBUG4("unbound event %p - UB_EV_PERSIST not set - Removing events for FD %i", ev, ev->fd);
208 PERROR("unbound event %p - De-registration failed for FD %i", ev, ev->fd);
209 }
210 }
211}
212
213/** Unbound FD errored
214 *
215 * libunbound doesn't request errors, so tell it a timeout occurred
216 *
217 * Because we don't have the separation between the IO event loop
218 * and the event loop processing results, we call ub_process
219 * immediately after calling the IO callback.
220 */
222 int fd, UNUSED int flags, int fd_errno, void *uctx)
223{
224 unbound_io_event_t *ev = talloc_get_type_abort(uctx, unbound_io_event_t);
225
226 fr_assert(ev->active); /* must be active */
227
228 DEBUG4("unbound event %p - FD %i errored: %s", ev, fd, fr_syserror(fd_errno));
229
230 /*
231 * Delete the timer as we're telling libunbound
232 * that it fired. This is imperfect but unbound
233 * doesn't have a callback for receiving errors.
234 */
236
237 ev->cb(-1, UB_EV_TIMEOUT, ev->uctx); /* Call libunbound - pretend this is a timeout */
238}
239
240
241/** Activate FD events and set a timer for a timeout
242 *
243 */
244static int _unbound_io_event_activate(struct ub_event *ub_ev, struct timeval *tv)
245{
246 unbound_io_event_t *ev = talloc_get_type_abort(ub_ev, unbound_io_event_t);
247
248 fr_assert(!ev->active); /* must not be active */
249
250 /*
251 * File descriptor event
252 */
253 if ((ev->events & UB_EV_READ) && (ev->events & UB_EV_WRITE)) {
254 fr_assert(ev->fd >= 0); /* File descriptor must valid */
255
256 DEBUG4("unbound event %p - Registered for read+write events on FD %i", ev, ev->fd);
257
258 if (fr_event_fd_insert(ev, NULL, ev->ev_b->el, ev->fd,
262 ev) < 0) {
263 PERROR("unbound event %p - Registration failed for read+write+error events on FD %i",
264 ev, ev->fd);
265
266 return -1;
267 }
268 } else if (ev->events & UB_EV_READ) {
269 fr_assert(ev->fd >= 0); /* File descriptor must valid */
270
271 DEBUG4("unbound event %p - Registered for read+error events on FD %i", ev, ev->fd);
272
273 if (fr_event_fd_insert(ev, NULL, ev->ev_b->el, ev->fd,
275 NULL,
277 ev) < 0) {
278 PERROR("unbound event %p - Registration failed for read+error events on FD %i",
279 ev, ev->fd);
280
281 return -1;
282 }
283 } else if (ev->events & UB_EV_WRITE) {
284 fr_assert(ev->fd >= 0); /* File descriptor must valid */
285
286 DEBUG4("unbound event %p - Registered for write+error events on FD %i", ev, ev->fd);
287
288 if (fr_event_fd_insert(ev, NULL, ev->ev_b->el, ev->fd,
289 NULL,
292 ev) < 0) {
293 PERROR("unbound event %p - Registration failed for write+error events on FD %i",
294 ev, ev->fd);
295
296 return -1;
297 }
298 }
299
300 /*
301 * Add a timeout event
302 */
303 if (ev->events & UB_EV_TIMEOUT) {
305
306 DEBUG4("unbound event %p - Timeout in %pV seconds", ev, fr_box_time_delta(timeout));
307
308 if (fr_timer_in(ev, ev->ev_b->el->tl, &ev->timer_ev,
309 timeout, false, _unbound_io_service_timer_expired, ev) < 0) {
310 PERROR("unbound event %p - Failed adding timeout", ev);
311
312 if (ev->events & (UB_EV_READ | UB_EV_WRITE)) {
314 }
315
316 return -1;
317 }
318 }
319
320 ev->active = true; /* Event is now active! */
321
322 return 0;
323}
324
325/* Deactivate FD events and disarm the timeout
326 *
327 */
328static int _unbound_io_event_deactivate(struct ub_event *ub_ev)
329{
330 unbound_io_event_t *ev = talloc_get_type_abort(ub_ev, unbound_io_event_t);
331 int ret = 0;
332
333 if (!ev->active) return 0; /* Allow this to be called multiple times */
334
335 if (ev->events & (UB_EV_READ | UB_EV_WRITE)) {
336 DEBUG4("unbound event %p - De-registering FD %i", ev, ev->fd);
337
338 if (fr_event_fd_delete(ev->ev_b->el, ev->fd, FR_EVENT_FILTER_IO) < 0) {
339 PERROR("unbound event %p - De-registration failed for FD %i", ev, ev->fd);
340
341 ret = -1;
342 }
343 }
344
345 if (ev->events & UB_EV_TIMEOUT) {
346 DEBUG4("unbound event %p - Disarming timeout", ev);
347
349 }
350
351 ev->active = false; /* Event is now inactive and can be modified */
352
353 return ret;
354}
355
356/** Modify an existing timeout
357 *
358 */
359static int _unbound_io_timer_modify(struct ub_event *ub_ev, UNUSED struct ub_event_base *ev_b,
360 void (*cb)(int, short, void*),
361 void *uctx, struct timeval *tv)
362{
363 unbound_io_event_t *ev = talloc_get_type_abort(ub_ev, unbound_io_event_t);
364 int ret = 0;
365 fr_time_delta_t timeout;
366
367 fr_assert(ev->events & UB_EV_TIMEOUT);
368
369 if (ev->cb != cb) {
370 DEBUG4("unbound event %p - New callback %p (old callback was %p)",
371 ev, cb, ev->cb);
372 ev->cb = cb;
373 }
374 if (ev->uctx != uctx) {
375 DEBUG4("unbound event %p - New uctx %p (old uctx was %p)",
376 ev, uctx, ev->uctx);
377 ev->uctx = uctx;
378 }
380
381 timeout = fr_time_delta_from_timeval(tv);
382
383 DEBUG4("unbound event %p - Timeout in %pV seconds", ev, fr_box_time_delta(timeout));
384
385 if (fr_timer_in(ev, ev->ev_b->el->tl, &ev->timer_ev,
386 timeout,
387 false, _unbound_io_service_timer_expired, ev) < 0) {
388 PERROR("unbound event %p - Failed adding timeout", ev);
389
390 ret = -1;
391 }
392
393 return ret;
394}
395
396/** Deactivate a timeout
397 *
398 */
399static int _unbound_io_timer_deactivate(struct ub_event *ub_ev)
400{
401 unbound_io_event_t *ev = talloc_get_type_abort(ub_ev, unbound_io_event_t);
402
403 fr_assert(ev->events & UB_EV_TIMEOUT);
404
405 DEBUG4("unbound event %p - Disarming timeout", ev);
406
408
409 return 0;
410}
411
412/** Returns a new libunbound event handle
413 *
414 * This handle is used by libunbound to interface with the worker's event loop
415 */
416static struct ub_event *_unbound_io_event_new(struct ub_event_base* base, int fd, short flags,
417 void (*cb)(int, short, void*), void *uctx)
418{
419 unbound_io_event_base_t *ev_b = talloc_get_type_abort(base, unbound_io_event_base_t);
421
422 static struct ub_event_vmt vmt = {
423 .add_bits = _unbound_io_event_flags_add,
424 .del_bits = _unbound_io_event_flags_del,
425 .set_fd = _unbound_io_event_fd_set,
429 .add_timer = _unbound_io_timer_modify,
431 };
432
433 MEM(ev = talloc_zero(ev_b, unbound_io_event_t));
434 ev->base.magic = UB_EVENT_MAGIC; /* Magic value libunbound requires */
435 ev->base.vmt = &vmt; /* Callbacks for adding/removing timers/fd events */
436 ev->ev_b = ev_b; /* Our event base (containing the el ) */
437 ev->events = flags; /* When this event should fire */
438 ev->fd = fd; /* File descriptor to register events for */
439 ev->cb = cb; /* Callback to execute on event */
440 ev->uctx = uctx; /* Lib unbound's arg to pass to the cb */
441 ev->active = false; /* Event is not currently active */
442
443 DEBUG4("unbound event %p - Allocated - Events %i, FD %i, callback %p, uctx %p", ev, flags, fd, cb, uctx);
444
445 return (struct ub_event *)ev;
446}
447
449{
450 if (ev_b->ub) ub_ctx_delete(ev_b->ub);
451
452 return 0;
453}
454
455/** Alloc a new event base, and unbound ctx initialised from that event base
456 *
457 * The ub_ctx is configured to use the el specified.
458 *
459 * When the thread ctx is freed, unbound_io_free should be called to gracefully
460 * free the ub_ctx, and then the event base structure it depends on.
461 *
462 * @param[in] ctx Talloc ctx to allocate even base in.
463 * @param[out] ev_b_out Event base. Free with talloc_free.
464 * @param[in] el To use to run the unbound event loop.
465 * @return
466 * - 0 on success.
467 * - -1 on failure.
468 */
470{
472
473 static struct ub_event_base_vmt vmt = {
474 .new_event = _unbound_io_event_new
475 };
476
477 /*
478 * Should be manually freed *AFTER* t->ub
479 * is freed. So must be parented from the NULL
480 * ctx.
481 */
482 MEM(ev_b = talloc_zero(ctx, unbound_io_event_base_t));
483 ev_b->base.magic = UB_EVENT_MAGIC;
484 ev_b->base.vmt = &vmt;
485 ev_b->el = el;
486
487 /*
488 * Create the main ub_ctx using our event base
489 * which specifies how libunbound integrates
490 * with our event loop.
491 */
492 ev_b->ub = ub_ctx_create_ub_event((struct ub_event_base *)ev_b);
493 if (!ev_b->ub) {
494 ERROR("Failed creating ub_ctx");
495 TALLOC_FREE(ev_b);
496 return -1;
497 }
498 talloc_set_destructor(ev_b, _event_base_free);
499
500 *ev_b_out = ev_b;
501
502 return 0;
503}
#define RCSID(id)
Definition build.h:506
#define DIAG_ON(_x)
Definition build.h:481
#define UNUSED
Definition build.h:336
#define DIAG_OFF(_x)
Definition build.h:480
#define MEM(x)
Definition debug.h:46
#define ERROR(fmt,...)
Definition dhcpclient.c:40
#define fr_event_fd_insert(...)
Definition event.h:247
@ FR_EVENT_FILTER_IO
Combined filter for read/write functions/.
Definition event.h:83
talloc_free(hp)
#define PERROR(_fmt,...)
Definition log.h:228
#define DEBUG4(_fmt,...)
Definition log.h:267
int fr_event_fd_delete(fr_event_list_t *el, int fd, fr_event_filter_t filter)
Remove a file descriptor from the event loop.
Definition event.c:1203
Stores all information relating to an event list.
Definition event.c:377
static void _unbound_io_service_errored(UNUSED fr_event_list_t *el, int fd, UNUSED int flags, int fd_errno, void *uctx)
Unbound FD errored.
Definition io.c:221
static void _unbound_io_event_flags_del(struct ub_event *ub_ev, short flags)
Alter the enabled flags associated with the event.
Definition io.c:105
int fd
File descriptor this event handle relates to.
Definition io.c:71
static void _unbound_io_service_timer_expired(UNUSED fr_timer_list_t *tl, UNUSED fr_time_t now, void *uctx)
Timeout fired.
Definition io.c:153
static int _unbound_io_timer_deactivate(struct ub_event *ub_ev)
Deactivate a timeout.
Definition io.c:399
short events
The events this event handle should receive when activated.
Definition io.c:68
struct ub_event base
Unbound event base, which we populate with callback functions for adding events for FDs setting timer...
Definition io.c:56
static void _unbound_io_service_writable(fr_event_list_t *el, int fd, UNUSED int flags, void *uctx)
Unbound FD became writable.
Definition io.c:192
int unbound_io_init(TALLOC_CTX *ctx, unbound_io_event_base_t **ev_b_out, fr_event_list_t *el)
Alloc a new event base, and unbound ctx initialised from that event base.
Definition io.c:469
void(* unbound_cb_t)(int, short flags, void *uctx)
Definition for libunbound's event callback.
Definition io.c:47
unbound_cb_t cb
The callback we need to call when a specified event happens.
Definition io.c:73
void * uctx
This is the argument libunbound wants passed to the callback when it's called.
Definition io.c:76
static void _unbound_io_service_readable(fr_event_list_t *el, int fd, UNUSED int flags, void *uctx)
Unbound FD became readable.
Definition io.c:168
static void _unbound_io_event_free(struct ub_event *ub_ev)
Free an event, and, by the magic of talloc, any timers or fd events.
Definition io.c:137
unbound_io_event_base_t * ev_b
Event base this handle was created for.
Definition io.c:61
static int _unbound_io_timer_modify(struct ub_event *ub_ev, UNUSED struct ub_event_base *ev_b, void(*cb)(int, short, void *), void *uctx, struct timeval *tv)
Modify an existing timeout.
Definition io.c:359
static int _unbound_io_event_deactivate(struct ub_event *ub_ev)
Definition io.c:328
static int _unbound_io_event_activate(struct ub_event *ub_ev, struct timeval *tv)
Activate FD events and set a timer for a timeout.
Definition io.c:244
static void _unbound_io_event_flags_add(struct ub_event *ub_ev, short flags)
Alter the enabled flags associated with the event.
Definition io.c:89
fr_timer_t * timer_ev
Stores the pointer to the enabled timer for this event handled.
Definition io.c:63
static int _event_base_free(unbound_io_event_base_t *ev_b)
Definition io.c:448
static struct ub_event * _unbound_io_event_new(struct ub_event_base *base, int fd, short flags, void(*cb)(int, short, void *), void *uctx)
Returns a new libunbound event handle.
Definition io.c:416
static void _unbound_io_event_fd_set(struct ub_event *ub_ev, int fd)
Change the file descriptor associated with an event.
Definition io.c:121
bool active
Whether this event is considered active.
Definition io.c:82
Wrapper around event handle for our event loop.
Definition io.c:55
Function prototypes and datatypes for the REST (HTTP) transport.
struct ub_ctx * ub
Unbound ctx instantiated from this event base.
Definition io.h:51
struct ub_event_base base
Interface structure for libunbound.
Definition io.h:49
fr_event_list_t * el
Event loop events should be inserted into.
Definition io.h:53
Wrapper around our event loop specifying callbacks for creating new event handles.
Definition io.h:48
#define fr_assert(_expr)
Definition rad_assert.h:37
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
Definition syserror.c:243
static fr_time_delta_t fr_time_delta_from_timeval(struct timeval const *tv)
Definition time.h:597
A time delta, a difference in time measured in nanoseconds.
Definition time.h:80
"server local" time.
Definition time.h:69
An event timer list.
Definition timer.c:49
A timer event.
Definition timer.c:83
#define FR_TIMER_DISARM_RETURN(_ev)
Definition timer.h:98
#define fr_timer_in(...)
Definition timer.h:87
#define FR_TIMER_DISARM(_ev)
Definition timer.h:91
static fr_event_list_t * el
#define fr_box_time_delta(_val)
Definition value.h:366