The FreeRADIUS server $Id: f3670dba8951ca10eb4948feb3dc3db9423a334f $
Loading...
Searching...
No Matches
network.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
5 * (at 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: 3e608c896afce1e48a505fe35e8e29fba813238c $
19 *
20 * @brief Receiver of socket data, which sends messages to the workers.
21 * @file io/network.c
22 *
23 * @copyright 2016 Alan DeKok (aland@freeradius.org)
24 */
25RCSID("$Id: 3e608c896afce1e48a505fe35e8e29fba813238c $")
26
27#define LOG_PREFIX nr->name
28
29#define LOG_DST nr->log
30
31#include <freeradius-devel/util/event.h>
32#include <freeradius-devel/util/rand.h>
33#include <freeradius-devel/util/rb.h>
34#include <freeradius-devel/util/syserror.h>
35#include <freeradius-devel/util/atexit.h>
36
37#include <freeradius-devel/io/channel.h>
38#include <freeradius-devel/io/listen.h>
39#include <freeradius-devel/io/network.h>
40#include <freeradius-devel/io/queue.h>
41
42#define MAX_WORKERS 64
43
45
52
53/** Associate a worker thread with a network thread
54 *
55 */
56typedef struct {
57 fr_heap_index_t heap_id; //!< workers are in a heap
58 fr_time_delta_t cpu_time; //!< how much CPU time this worker has spent
59 fr_time_delta_t predicted; //!< predicted processing time for one packet
60
61 bool blocked; //!< is this worker blocked?
62
63 fr_channel_t *channel; //!< channel to the worker
64 fr_message_set_t *reply_ms; //!< message set the worker will use to send reply data.
65 fr_worker_t *worker; //!< worker pointer
68
69typedef struct {
70 fr_rb_node_t listen_node; //!< rbtree node for looking up by listener.
71 fr_rb_node_t num_node; //!< rbtree node for looking up by number.
72
73 fr_network_t *nr; //!< O(N) issues in talloc
74 int number; //!< unique ID
75 fr_heap_index_t heap_id; //!< for the sockets_by_num heap
76
77 fr_event_filter_t filter; //!< what type of filter it is
78 fr_event_fd_t *ef; //!< the I/O event, if it was inserted. Parented off
79 ///< this socket, so it cannot outlive it.
80
81 bool dead; //!< is it dead?
82 bool blocked; //!< is it blocked?
83 bool closed; //!< the descriptor has been closed.
84
85 unsigned int outstanding; //!< number of outstanding packets sent to the worker
86 fr_listen_t *listen; //!< I/O ctx and functions.
87
88 fr_message_set_t *ms; //!< message buffers for this socket.
89 fr_channel_data_t *cd; //!< cached in case of allocation & read error
90 size_t leftover; //!< leftover data from a previous read
91 size_t written; //!< however much we did in a partial write
92
93 fr_channel_data_t *pending; //!< the currently pending partial packet
94 fr_heap_t *waiting; //!< packets waiting to be written
97
98/*
99 * We have an array of workers, so we can index the workers in
100 * O(1) time. remove the heap of "workers ordered by CPU time"
101 * when we send a packet to a worker, just update the predicted
102 * CPU time in place. when we receive a reply from a worker,
103 * just update the predicted CPU time in place.
104 *
105 * when we need to choose a worker, pick 2 at random, and then
106 * choose the one with the lowe cpu time. For background, see
107 * "Power of Two-Choices" and
108 * https://www.eecs.harvard.edu/~michaelm/postscripts/mythesis.pdf
109 * https://www.eecs.harvard.edu/~michaelm/postscripts/tpds2001.pdf
110 */
112 char const *name; //!< Network ID for logging.
113
114 pthread_t thread_id; //!< for self
115
116 bool suspended; //!< whether or not we're suspended.
117
118 fr_log_t const *log; //!< log destination
119 fr_log_lvl_t lvl; //!< debug log level
120
121 fr_atomic_queue_t *aq_control; //!< atomic queue for control messages sent to me
122
123 fr_control_t *control; //!< the control plane
124
125 fr_ring_buffer_t *rb; //!< ring buffer for my control-plane messages
126
127 fr_event_list_t *el; //!< our event list
128
129 fr_heap_t *replies; //!< replies from the worker, ordered by priority / origin time
130
132
133 fr_rb_tree_t *sockets; //!< list of sockets we're managing, ordered by the listener
134 fr_rb_tree_t *sockets_by_num; //!< ordered by number;
135
136 int num_workers; //!< number of active workers
137 int num_blocked; //!< number of blocked workers
138 int num_pending_workers; //!< number of workers we're waiting to start.
139 int max_workers; //!< maximum number of allowed workers
140 int num_sockets; //!< actually a counter...
141
142 int signal_pipe[2]; //!< Pipe for signalling the worker in an orderly way.
143 ///< This is more deterministic than using async signals.
144
145 bool exiting; //!< are we exiting?
146
147 fr_network_config_t config; //!< configuration
149};
150
151static void fr_network_post_event(fr_event_list_t *el, fr_time_t now, void *uctx);
152static int fr_network_pre_event(fr_time_t now, fr_time_delta_t wake, void *uctx);
155static void fr_network_read(UNUSED fr_event_list_t *el, int sockfd, UNUSED int flags, void *ctx);
156
157static fr_cmp_ret_t reply_cmp(void const *one, void const *two)
158{
159 fr_channel_data_t const *a = one, *b = two;
160 int ret;
161
162 ret = CMP(a->priority, b->priority);
163 if (ret != 0) return ret;
164
165 return fr_time_cmp(a->m.when, b->m.when);
166}
167
168static fr_cmp_ret_t waiting_cmp(void const *one, void const *two)
169{
170 fr_channel_data_t const *a = one, *b = two;
171 int ret;
172
173 ret = CMP(a->priority, b->priority);
174 if (ret != 0) return ret;
175
176 return fr_time_cmp(a->reply.request_time, b->reply.request_time);
177}
178
179static fr_cmp_ret_t socket_listen_cmp(void const *one, void const *two)
180{
181 fr_network_socket_t const *a = one, *b = two;
182
183 return CMP(a->listen, b->listen);
184}
185
186static fr_cmp_ret_t socket_num_cmp(void const *one, void const *two)
187{
188 fr_network_socket_t const *a = one, *b = two;
189
190 return CMP(a->number, b->number);
191}
192
193/*
194 * Explicitly cleanup the memory allocated to the ring buffer,
195 * just in case valgrind complains about it.
196 */
197static int _fr_network_rb_free(void *arg)
198{
199 return talloc_free(arg);
200}
201
202/** Initialise thread local storage
203 *
204 * @return fr_ring_buffer_t for messages
205 */
207{
209
210 rb = fr_network_rb;
211 if (rb) return rb;
212
214 if (!rb) {
215 fr_perror("Failed allocating memory for network ring buffer");
216 return NULL;
217 }
218
220
221 return rb;
222}
223
224static inline bool is_network_thread(fr_network_t const *nr)
225{
226 return (pthread_equal(pthread_self(), nr->thread_id) != 0);
227}
228
230
231/** Add a fr_listen_t to a network
232 *
233 * @param nr the network
234 * @param li the listener
235 */
237{
239
240 /*
241 * Associate the protocol dictionary with the listener, so that the decode functions can check /
242 * use it.
243 *
244 * A virtual server may start off with a "dictionary" block, and therefore define a local
245 * dictionary. So the "root" dictionary of a virtual server may not be a protocol dict.
246 */
247 fr_assert(li->server_cs != NULL);
249
250 fr_assert(li->dict != NULL);
251
252 /*
253 * Skip a bunch of work if we're already in the network thread.
254 */
255 if (is_network_thread(nr) && !li->needs_full_setup) {
256 return fr_network_listen_add_self(nr, li);
257 }
258
259 rb = fr_network_rb_init();
260 if (!rb) return -1;
261
262 return fr_control_message_send(nr->control, rb, FR_CONTROL_ID_LISTEN, &li, sizeof(li));
263}
264
265
266/** Delete a socket from a network. MUST be called only by the listener itself!.
267 *
268 * @param nr the network
269 * @param li the listener
270 */
272{
274
276
277 fr_rb_find((void **)&s, nr->sockets, &(fr_network_socket_t){ .listen = li });
278 if (!s) return -1;
279
281
282 return 0;
283}
284
285/** Add a "watch directory" call to a network
286 *
287 * @param nr the network
288 * @param li the listener
289 */
291{
293
294 rb = fr_network_rb_init();
295 if (!rb) return -1;
296
297 return fr_control_message_send(nr->control, rb, FR_CONTROL_ID_DIRECTORY, &li, sizeof(li));
298}
299
300/** Add a worker to a network in a different thread
301 *
302 * @param nr the network
303 * @param worker the worker
304 */
306{
308
309 rb = fr_network_rb_init();
310 if (!rb) return -1;
311
312 (void) talloc_get_type_abort(nr, fr_network_t);
313 (void) talloc_get_type_abort(worker, fr_worker_t);
314
315 return fr_control_message_send(nr->control, rb, FR_CONTROL_ID_WORKER, &worker, sizeof(worker));
316}
317
318static void fr_network_worker_started_callback(void const *data, size_t data_size, fr_time_t now, void *uctx);
319
320/** Add a worker to a network in the same thread
321 *
322 * @param nr the network
323 * @param worker the worker
324 */
326{
327 fr_network_worker_started_callback(&worker, sizeof(worker), fr_time_wrap(0), nr);
328}
329
330
331/** Signal the network to read from a listener
332 *
333 * @param nr the network
334 * @param li the listener to read from
335 */
337{
339
340 (void) talloc_get_type_abort(nr, fr_network_t);
342
343 fr_rb_find((void **)&s, nr->sockets, &(fr_network_socket_t){ .listen = li });
344 if (!s) return;
345
346 /*
347 * Go read the socket.
348 */
349 fr_network_read(nr->el, s->listen->fd, 0, s);
350}
351
352
353/** Inject a packet for a listener to write
354 *
355 * @param nr the network
356 * @param li the listener where the packet is being injected
357 * @param packet the packet to be written
358 * @param packet_len the length of the packet
359 * @param packet_ctx The packet context to write
360 * @param request_time when the packet was received.
361 */
362void fr_network_listen_write(fr_network_t *nr, fr_listen_t *li, uint8_t const *packet, size_t packet_len,
363 void *packet_ctx, fr_time_t request_time)
364{
365 fr_message_t *lm;
367
368 cd = (fr_channel_data_t) {
369 .m = (fr_message_t) {
371 .data_size = packet_len,
372 .when = request_time,
373 },
374
375 .channel = {
376 .heap_id = FR_HEAP_INDEX_INVALID,
377 },
378
379 .listen = li,
380 .priority = PRIORITY_NOW,
381 .reply.request_time = request_time,
382 };
383
384 memcpy(&cd.m.data, &packet, sizeof(packet)); /* const issues */
385 memcpy(&cd.packet_ctx, &packet_ctx, sizeof(packet_ctx)); /* const issues */
386
387 /*
388 * Localize the message and insert it into the heap of pending messages.
389 */
390 lm = fr_message_localize(nr, &cd.m, sizeof(cd));
391 if (!lm) return;
392
393 if (fr_heap_insert(&nr->replies, lm) < 0) {
394 fr_message_done(lm);
395 }
396}
397
398
399/** Inject a packet for a listener to read
400 *
401 * @param nr the network
402 * @param li the listener where the packet is being injected
403 * @param packet the packet to be injected
404 * @param packet_len the length of the packet
405 * @param recv_time when the packet was received.
406 * @return
407 * - <0 on error
408 * - 0 on success
409 */
410int fr_network_listen_inject(fr_network_t *nr, fr_listen_t *li, uint8_t const *packet, size_t packet_len, fr_time_t recv_time)
411{
412 int rcode;
414 fr_network_inject_t my_inject;
415
416 /*
417 * Can't inject to injection-less destinations.
418 */
419 if (!li->app_io->inject) {
420 fr_strerror_const("Listener cannot accept injected packet");
421 return -1;
422 }
423
424 /*
425 * Avoid a bounce through the event loop if we're being called from the network thread.
426 */
427 if (is_network_thread(nr)) {
429
430 fr_rb_find((void **)&s, nr->sockets, &(fr_network_socket_t){ .listen = li });
431 if (!s) {
432 fr_strerror_const("Listener was not found for injected packet");
433 return -1;
434 }
435
436 /*
437 * Inject the packet. The master.c mod_read() routine will then take care of avoiding
438 * IO, and instead return the packet to the network side.
439 */
440 if (li->app_io->inject(li, packet, packet_len, recv_time) == 0) {
441 (void) fr_network_read(nr->el, li->fd, 0, s);
442 }
443
444 return 0;
445 }
446
447 rb = fr_network_rb_init();
448 if (!rb) return -1;
449
450 my_inject.listen = li;
451 MEM(my_inject.packet = talloc_memdup(NULL, packet, packet_len));
452 my_inject.packet_len = packet_len;
453 my_inject.recv_time = recv_time;
454
455 rcode = fr_control_message_send(nr->control, rb, FR_CONTROL_ID_INJECT, &my_inject, sizeof(my_inject));
456 if (rcode < 0) talloc_free(my_inject.packet);
457
458 return rcode;
459}
460
463 { 0 }
464};
465
468 { 0 }
469};
470
473 { 0 }
474};
475
478 { 0 }
479};
480
482{
485
486 if (nr->suspended) return;
487
488 for (s = fr_rb_iter_init_inorder(nr->sockets, &iter);
489 s != NULL;
490 s = fr_rb_iter_next_inorder(nr->sockets, &iter)) {
492 PERROR("Failed removing read callback from event loop");
494 }
495 }
496 nr->suspended = true;
497}
498
500{
503
504 if (!nr->suspended) return;
505
506 for (s = fr_rb_iter_init_inorder(nr->sockets, &iter);
507 s != NULL;
508 s = fr_rb_iter_next_inorder(nr->sockets, &iter)) {
510 PERROR("Failed adding read callback to event loop");
512 }
513 }
514 nr->suspended = false;
515}
516
517#define IALPHA (8)
518#define RTT(_old, _new) fr_time_delta_wrap((fr_time_delta_unwrap(_new) + (fr_time_delta_unwrap(_old) * (IALPHA - 1))) / IALPHA)
519
520/** Callback which handles a message being received on the network side.
521 *
522 * @param[in] ch the channel that the message is on.
523 * @param[in] cd the message (if any) to start with
524 * @param[in] uctx the network
525 */
526static void fr_network_recv_reply(fr_channel_t *ch, fr_channel_data_t *cd, void *uctx)
527{
528 fr_network_t *nr = uctx;
529 fr_network_worker_t *worker;
530
531 cd->channel.ch = ch;
532
533 /*
534 * Update stats for the worker.
535 */
537 worker->stats.out++;
538 worker->cpu_time = cd->reply.cpu_time;
539 if (!fr_time_delta_ispos(worker->predicted)) {
540 worker->predicted = cd->reply.processing_time;
541 } else {
542 worker->predicted = RTT(worker->predicted, cd->reply.processing_time);
543 }
544
545 /*
546 * Unblock the worker.
547 */
548 if (worker->blocked) {
549 worker->blocked = false;
550 nr->num_blocked--;
552 }
553
554 /*
555 * Ensure that heap insert works.
556 */
557 cd->channel.heap_id = FR_HEAP_INDEX_INVALID;
558 if (fr_heap_insert(&nr->replies, cd) < 0) {
559 fr_message_done(&cd->m);
560 fr_assert(0 == 1);
561 }
562}
563
564/** Handle a network control message callback for a channel
565 *
566 * This is called from the event loop when we get a notification
567 * from the event signalling pipe.
568 *
569 * @param[in] data the message
570 * @param[in] data_size size of the data
571 * @param[in] now the current time
572 * @param[in] uctx the network
573 */
574static void fr_network_channel_callback(void const *data, size_t data_size, fr_time_t now, void *uctx)
575{
577 fr_channel_t *ch;
578 fr_network_t *nr = uctx;
579
580 ce = fr_channel_service_message(now, &ch, NULL, data, data_size);
581 DEBUG3("Channel %s",
582 fr_table_str_by_value(channel_signals, ce, "<INVALID>"));
583 switch (ce) {
584 case FR_CHANNEL_ERROR:
585 return;
586
587 case FR_CHANNEL_EMPTY:
588 return;
589
590 case FR_CHANNEL_NOOP:
591 break;
592
594 fr_assert(ch != NULL);
595 while (fr_channel_recv_reply(ch));
596 break;
597
599 fr_assert(0 == 1);
600 break;
601
602 case FR_CHANNEL_OPEN:
603 fr_assert(0 == 1);
604 break;
605
606 case FR_CHANNEL_CLOSE:
607 {
608 fr_network_worker_t *w = talloc_get_type_abort(fr_channel_requestor_uctx_get(ch),
610 int i;
611
612 /*
613 * Remove this worker from the array
614 */
615 DEBUG3("Worker acked our close request");
616 for (i = 0; i < nr->num_workers; i++) {
617 if (nr->workers[i] == w) {
618 if (i == (nr->num_workers - 1)) break;
619
620 /*
621 * Close the hole...
622 */
623 memmove(&nr->workers[i], &nr->workers[i + 1],
624 (uint8_t *) &nr->workers[nr->num_workers] - (uint8_t *) &nr->workers[i + 1]);
625 break;
626 }
627 }
628 nr->num_workers--;
629 nr->workers[nr->num_workers] = NULL; /* over-write now unused pointer */
630 }
631 break;
632 }
633}
634
635#define OUTSTANDING(_x) ((_x)->stats.in - (_x)->stats.out)
636
637/** Send a message on the "best" channel.
638 *
639 * @param nr the network
640 * @param cd the message we've received
641 */
643{
644 fr_network_worker_t *worker;
645
646 (void) talloc_get_type_abort(nr, fr_network_t);
647
648 /*
649 * The workers have been signalled to close and are tearing their
650 * channels down. Anything queued now is stranded: the worker
651 * discards it without a reply, so our outstanding count for the
652 * socket never comes back down and the socket can never be freed.
653 *
654 * The listeners are closed before the close is signalled, so a
655 * socket read cannot get here. Assert, so that whatever did shows
656 * itself with a backtrace, and drop the packet in release builds.
657 */
658 if (!fr_cond_assert_msg(!nr->exiting,
659 "Sending packet to worker after signalling the channel close")) return -1;
660
661 if (!nr->num_workers) {
662 RATE_LIMIT_GLOBAL(ERROR, "Failed sending packet to worker - "
663 "No workers are available");
664 return -1;
665 }
666
667retry:
668 if (nr->num_workers == 1) {
669 worker = nr->workers[0];
670 if (worker->blocked) {
671 RATE_LIMIT_GLOBAL(ERROR, "Failed sending packet to worker - "
672 "In single-threaded mode and worker is blocked");
673 drop:
674 worker->stats.dropped++;
675 return -1;
676 }
677
678 } else if (nr->num_blocked == 0) {
679 int64_t cmp;
680 uint32_t one, two;
681
682 one = fr_rand() % nr->num_workers;
683 do {
684 two = fr_rand() % nr->num_workers;
685 } while (two == one);
686
687 /*
688 * Choose a worker based on minimizing the amount
689 * of future work it's being asked to do.
690 *
691 * If both workers have the same number of
692 * outstanding requests, then choose the worker
693 * which has used the least total CPU time.
694 */
695 cmp = (OUTSTANDING(nr->workers[one]) - OUTSTANDING(nr->workers[two]));
696 if (cmp < 0) {
697 worker = nr->workers[one];
698
699 } else if (cmp > 0) {
700 worker = nr->workers[two];
701
702 } else if (fr_time_delta_lt(nr->workers[one]->cpu_time, nr->workers[two]->cpu_time)) {
703 worker = nr->workers[one];
704
705 } else {
706 worker = nr->workers[two];
707 }
708 } else {
709 int i;
710 uint64_t min_outstanding = UINT64_MAX;
711 fr_network_worker_t *found = NULL;
712
713 /*
714 * Some workers are blocked. Pick the worker
715 * with the least amount of future work to do.
716 */
717 for (i = 0; i < nr->num_workers; i++) {
718 uint64_t outstanding;
719
720 worker = nr->workers[i];
721 if (worker->blocked) continue;
722
723 outstanding = OUTSTANDING(worker);
724 if ((outstanding < min_outstanding) || !found) {
725 found = worker;
726 min_outstanding = outstanding;
727
728 } else if (outstanding == min_outstanding) {
729 /*
730 * Queue lengths are the same.
731 * Choose this worker if it's
732 * less busy than the previous one we found.
733 */
734 if (fr_time_delta_lt(worker->cpu_time, found->cpu_time)) {
735 found = worker;
736 }
737 }
738 }
739
740 if (!found) {
741 RATE_LIMIT_GLOBAL(PERROR, "Failed sending packet to worker - Couldn't find active worker, "
742 "%u/%u workers are blocked", nr->num_blocked, nr->num_workers);
743 return -1;
744 }
745
746 worker = found;
747 }
748
749 (void) talloc_get_type_abort(worker, fr_network_worker_t);
750
751 /*
752 * Too many outstanding packets for this worker. Drop
753 * the request.
754 *
755 * If the worker we've picked has too many outstanding
756 * packets, then we have either only one worker, in which
757 * cae we should drop the packet. Or, we were unable to
758 * find a worker with smaller than max_outstanding
759 * packets. In which case all of the workers are likely
760 * at max_outstanding.
761 *
762 * In both cases, we should just drop the new packet.
763 */
764 fr_assert(worker->stats.in >= worker->stats.out);
765 if (nr->config.max_outstanding &&
766 (OUTSTANDING(worker) >= nr->config.max_outstanding)) {
767 RATE_LIMIT_GLOBAL(PERROR, "max_outstanding reached - dropping packet");
768 goto drop;
769 }
770
771 /*
772 * Send the message to the channel. If we fail, drop the
773 * packet. The only reason for failure is that the
774 * worker isn't servicing it's input queue. When that
775 * happens, we have no idea what to do, and the whole
776 * thing falls over.
777 */
778 if (fr_channel_send_request(worker->channel, cd) < 0) {
779 worker->stats.dropped++;
780 worker->blocked = true;
781 nr->num_blocked++;
782
783 RATE_LIMIT_GLOBAL(PERROR, "Failed sending packet to worker - %u/%u workers are blocked",
784 nr->num_blocked, nr->num_workers);
785
786 if (nr->num_blocked == nr->num_workers) {
788 return -1;
789 }
790 goto retry;
791 }
792
793 worker->stats.in++;
794
795 /*
796 * We're projecting that the worker will use more CPU
797 * time to process this request. The CPU time will be
798 * updated with a more accurate number when we receive a
799 * reply from this channel.
800 */
801 worker->cpu_time = fr_time_delta_add(worker->cpu_time, worker->predicted);
802
803 return 0;
804}
805
806
807/** Send a packet to the worker.
808 *
809 * MUST only be called from the network thread.
810 *
811 * @param nr the network
812 * @param parent the parent listener
813 * @param li the listener that the packet was "read" from. Can be "parent"
814 * @param buffer the packet to send
815 * @param buflen size of the packet to send
816 * @param recv_time of the packet
817 * @param packet_ctx for the packet
818 * @return
819 * - <0 on error
820 * - 0 on success
821 */
823 const uint8_t *buffer, size_t buflen, fr_time_t recv_time, void *packet_ctx)
824{
827
828 (void) talloc_get_type_abort(nr, fr_network_t);
830
831 fr_rb_find((void **)&s, nr->sockets, &(fr_network_socket_t){ .listen = li });
832 if (!s) return -1;
833
835 if (!cd) return -1;
836
837 cd->listen = parent;
840 cd->request.recv_time = recv_time;
841 memcpy(cd->m.data, buffer, buflen);
842 cd->m.when = fr_time();
843
844 if (fr_network_send_request(nr, cd) < 0) {
846 fr_message_done(&cd->m);
847 nr->stats.dropped++;
848 s->stats.dropped++;
849 return -1;
850 }
851
852 s->outstanding++;
853 return 0;
854}
855
856/** Get the number of outstanding packets
857 *
858 * @param nr the network
859 * @param li the listener that the packet was "read" from
860 * @return
861 * - <0 on error
862 * - the number of outstanding packets
863*/
866
867 (void) talloc_get_type_abort(nr, fr_network_t);
869
870 fr_rb_find((void **)&s, nr->sockets, &(fr_network_socket_t){ .listen = li });
871 if (!s) return -1;
872
873 return s->outstanding;
874}
875
876/*
877 * Mark it as dead, but DON'T free it until all of the replies
878 * have come in.
879 */
881{
882 int i;
883
884 if (s->dead) return;
885
886 s->dead = true;
887
888 /*
889 * Nothing writes to a dead socket - fr_network_post_event()
890 * discards its replies - so the descriptor goes now. The memory
891 * has to wait for the workers to finish with its messages.
892 */
893 if (network_socket_close(s) < 0) PWARN("Failed removing event for socket %d", s->number);
894
895 for (i = 0; i < nr->max_workers; i++) {
896 if (!nr->workers[i]) continue;
897
898 (void) fr_worker_listen_cancel(nr->workers[i]->worker, s->listen);
899 }
900
901 /*
902 * If there are no outstanding packets, then we can free
903 * it now.
904 */
905 if (!s->outstanding) {
906 talloc_free(s);
907 return;
908 }
909
910 /*
911 * There are still outstanding packets. Leave it in the
912 * socket tree, so that replies from the worker can find
913 * it. When we've received all of the replies, then
914 * fr_network_post_event() will clean up this socket.
915 */
916}
917
918/** Read a packet from the network.
919 *
920 * @param[in] el the event list.
921 * @param[in] sockfd the socket which is ready to read.
922 * @param[in] flags from kevent.
923 * @param[in] ctx the network socket context.
924 */
925static void fr_network_read(UNUSED fr_event_list_t *el, int sockfd, UNUSED int flags, void *ctx)
926{
927 int num_messages = 0;
928 fr_network_socket_t *s = ctx;
929 fr_network_t *nr = s->nr;
930 ssize_t data_size;
931 fr_channel_data_t *cd, *next;
932
933 if (!fr_cond_assert_msg(s->listen->fd == sockfd, "Expected listen->fd (%u) to be equal event fd (%u)",
934 s->listen->fd, sockfd)) return;
935
936 DEBUG3("Reading data from FD %u", sockfd);
937
938 if (!s->cd) {
940 if (!cd) {
941 ERROR("Failed allocating message size %zd! - Closing socket",
944 return;
945 }
946 } else {
947 cd = s->cd;
948 }
949
950 fr_assert(cd->m.data != NULL);
951
952next_message:
953 /*
954 * Poll this socket, but not too often. We have to go
955 * service other sockets, too.
956 */
957 if (num_messages > 16) {
958 s->cd = cd;
959 return;
960 }
961
963
964 /*
965 * Read data from the network.
966 *
967 * Return of 0 means "no data", which is fine for UDP.
968 * For TCP, if an underlying read() on the TCP socket
969 * returns 0, (which signals that the FD is no longer
970 * usable) this function should return -1, so that the
971 * network side knows that it needs to close the
972 * connection.
973 */
974 data_size = s->listen->app_io->read(s->listen, &cd->packet_ctx, &cd->request.recv_time,
975 cd->m.data, cd->m.rb_size, &s->leftover);
976 if (data_size == 0) {
977 /*
978 * Cache the message only when there are leftover
979 * bytes from a partial stream read. The buffer
980 * must be handed back on the next call so the
981 * stream can append to it.
982 *
983 * When app_io->read() calls fr_network_listen_send_packet()
984 * internally, that function calls fr_message_and_data_alloc()
985 * on the same message set. Because the message ring uses
986 * fr_ring_buffer_reserve() (which does not advance write_offset),
987 * the new alloc returns the same ring slot as our reservation.
988 * The memset inside message_reserve zeroes our struct, then the
989 * new message commits into that slot. We can detect this
990 * because cd->m.data_size is non-zero after the alloc commits.
991 *
992 * In the aliased case, cd now refers to the already-committed
993 * message that was dispatched to a worker. We must not reset
994 * or modify it; just clear s->cd so the next call reserves fresh.
995 *
996 * When neither leftover nor aliasing applies, the reservation
997 * is clean and unused; reset it explicitly so its ring-buffer
998 * slot is immediately reusable.
999 */
1000 if (s->leftover) {
1001 s->cd = cd;
1002 } else if (cd->m.data_size != 0) {
1003 s->cd = NULL;
1004 } else {
1005 fr_message_and_data_reset(s->ms, &cd->m);
1006 s->cd = NULL;
1007 }
1008 return;
1009 }
1010
1011 /*
1012 * Error: close the connection, and remove the fr_listen_t
1013 */
1014 if (data_size < 0) {
1015// fr_log(nr->log, L_DBG_ERR, "error from transport read on socket %d", sockfd);
1017 return;
1018 }
1019 s->cd = NULL;
1020
1021 DEBUG3("Read %zd byte(s) from FD %u", data_size, sockfd);
1022 if (s->listen->read_hexdump) HEXDUMP2(cd->m.data, data_size, "%s read ", s->listen->name);
1023 nr->stats.in++;
1024 s->stats.in++;
1025
1026 /*
1027 * Initialize the rest of the fields of the channel data.
1028 *
1029 * We always use "now" as the time of the message, as the
1030 * packet MAY be a duplicate packet magically resurrected
1031 * from the past. i.e. If the read routines are doing
1032 * dedup, then they notice that the packet is a
1033 * duplicate. In that case, they send over a copy of the
1034 * packet, BUT with the original timestamp. This
1035 * information tells the worker that the packet is a
1036 * duplicate.
1037 */
1038 cd->m.when = fr_time();
1039 cd->listen = s->listen;
1040
1041 /*
1042 * Nothing in the buffer yet. Allocate room for one
1043 * packet.
1044 */
1045 if ((cd->m.data_size == 0) && (!s->leftover)) {
1046 (void) fr_message_and_data_commit(s->ms, &cd->m, data_size);
1047 next = NULL;
1048 } else {
1049 /*
1050 * There are leftover bytes in the buffer, feed
1051 * them to the next round of reading.
1052 */
1053 if (s->leftover) {
1054 next = (fr_channel_data_t *) fr_message_and_data_commit_with_leftover(s->ms, &cd->m, data_size, s->leftover,
1056 if (!next) {
1057 PERROR("Failed reserving partial packet.");
1058 // @todo - probably close the socket...
1059 fr_assert(0 == 1);
1060 }
1061 } else {
1062 (void) fr_message_and_data_commit(s->ms, &cd->m, data_size);
1063 next = NULL;
1064 }
1065 }
1066
1067 /*
1068 * Set the priority. Which incidentally also checks if
1069 * we're allowed to read this particular kind of packet.
1070 *
1071 * That check is because the app_io handlers just read
1072 * packets, and don't really have access to the parent
1073 * "list of allowed packet types". So we have to do the
1074 * work here in a callback.
1075 *
1076 * That should probably be fixed...
1077 */
1078 if (s->listen->app->priority) {
1079 int priority;
1080
1081 priority = s->listen->app->priority(s->listen->app_instance, cd->m.data, data_size);
1082 if (priority <= 0) goto discard;
1083
1084 cd->priority = priority;
1085 }
1086
1087 if (fr_network_send_request(nr, cd) < 0) {
1088 discard:
1089 talloc_free(cd->packet_ctx); /* not sure what else to do here */
1090 fr_message_done(&cd->m);
1091 nr->stats.dropped++;
1092 s->stats.dropped++;
1093
1094 } else {
1095 /*
1096 * One more packet sent to a worker.
1097 */
1098 s->outstanding++;
1099 }
1100
1101 /*
1102 * If there is a next message, go read it from the buffer.
1103 *
1104 * @todo - note that this calls read(), even if the
1105 * app_io has paused the reader. We likely want to be
1106 * able to check that, too. We might just remove this
1107 * "goto"...
1108 */
1109 if (next) {
1110 cd = next;
1111 num_messages++;
1112 goto next_message;
1113 }
1114}
1115
1116int fr_network_sendto_worker(fr_network_t *nr, fr_listen_t *li, void *packet_ctx, uint8_t const *data, size_t data_len, fr_time_t recv_time)
1117{
1120
1121 fr_rb_find((void **)&s, nr->sockets, &(fr_network_socket_t){ .listen = li });
1122 if (!s) return -1;
1123
1124 cd = (fr_channel_data_t *) fr_message_and_data_alloc(s->ms, data_len);
1125 if (!cd) return -1;
1126
1127 s->stats.in++;
1128
1130
1131 cd->m.when = recv_time;
1132 cd->listen = li;
1133 cd->packet_ctx = packet_ctx;
1134
1135 memcpy(cd->m.data, data, data_len);
1136
1137 if (fr_network_send_request(nr, cd) < 0) {
1139 fr_message_done(&cd->m);
1140 nr->stats.dropped++;
1141 s->stats.dropped++;
1142 return -1;
1143 }
1144
1145 /*
1146 * One more packet sent to a worker.
1147 */
1148 s->outstanding++;
1149 return 0;
1150}
1151
1152
1153/** Get a notification that a vnode changed
1154 *
1155 * @param[in] el the event list.
1156 * @param[in] sockfd the socket which is ready to read.
1157 * @param[in] fflags from kevent.
1158 * @param[in] ctx the network socket context.
1159 */
1160static void fr_network_vnode_extend(UNUSED fr_event_list_t *el, int sockfd, int fflags, void *ctx)
1161{
1162 fr_network_socket_t *s = ctx;
1163 fr_network_t *nr = s->nr;
1164
1165 if (!fr_cond_assert(s->listen->fd == sockfd)) return;
1166
1167 DEBUG3("network vnode");
1168
1169 /*
1170 * Tell the IO handler that something has happened to the
1171 * file.
1172 */
1173 s->listen->app_io->vnode(s->listen, fflags);
1174}
1175
1176
1177/** Handle errors for a socket.
1178 *
1179 * @param[in] el the event list
1180 * @param[in] sockfd the socket which has a fatal error.
1181 * @param[in] flags returned by kevent.
1182 * @param[in] fd_errno returned by kevent.
1183 * @param[in] ctx the network socket context.
1184 */
1186 int fd_errno, void *ctx)
1187{
1188 fr_network_socket_t *s = ctx;
1189 fr_network_t *nr = s->nr;
1190
1191 if (s->listen->app_io->error) {
1192 s->listen->app_io->error(s->listen);
1193
1194 } else if (flags & EV_EOF) {
1195 DEBUG2("Socket %s closed by peer", s->listen->name);
1196
1197 } else {
1198 ERROR("Socket %s errored - %s", s->listen->name, fr_syserror(fd_errno));
1199 }
1200
1202}
1203
1204
1205/** Write packets to the network.
1206 *
1207 * @param el the event list
1208 * @param sockfd the socket which is ready to write
1209 * @param flags returned by kevent.
1210 * @param ctx the network socket context.
1211 */
1212static void fr_network_write(UNUSED fr_event_list_t *el, UNUSED int sockfd, UNUSED int flags, void *ctx)
1213{
1214 fr_network_socket_t *s = ctx;
1215 fr_listen_t *li = s->listen;
1216 fr_network_t *nr = s->nr;
1218
1219 (void) talloc_get_type_abort(nr, fr_network_t);
1220
1221 /*
1222 * Start with the currently pending message, and then
1223 * work through the priority heap.
1224 */
1225 if (s->pending) {
1226 cd = s->pending;
1227 s->pending = NULL;
1228
1229 } else {
1230 fr_heap_pop((void **)&cd, &s->waiting);
1231 }
1232
1233 while (cd != NULL) {
1234 int rcode;
1235
1236 fr_assert(li == cd->listen);
1237 if (li->write_hexdump) HEXDUMP2(cd->m.data, cd->m.data_size, "%s writing ", li->name);
1238 rcode = li->app_io->write(li, cd->packet_ctx,
1239 cd->reply.request_time,
1240 cd->m.data, cd->m.data_size, s->written);
1241
1242 /*
1243 * Write of 0 bytes means an OS bug, and we just discard this packet.
1244 */
1245 if (rcode == 0) {
1246 RATE_LIMIT_GLOBAL(ERROR, "Discarding packet due to write returning zero for socket %s",
1247 s->listen->name);
1248 goto discard;
1249 }
1250
1251 /*
1252 * Or we have a write error.
1253 */
1254 if (rcode < 0) {
1255 /*
1256 * Stop processing the heap, and set the
1257 * pending message to the current one.
1258 */
1259 if (errno == EWOULDBLOCK) {
1260 save_pending:
1261 fr_assert(!s->pending);
1262
1263 if (cd->m.status != FR_MESSAGE_LOCALIZED) {
1264 fr_message_t *lm;
1265
1266 lm = fr_message_localize(s, &cd->m, sizeof(*cd));
1267 if (!lm) {
1268 ERROR("Failed saving pending packet");
1269 goto dead;
1270 }
1271
1272 cd = (fr_channel_data_t *) lm;
1273 }
1274
1275 if (!s->blocked) {
1277 PERROR("Failed adding write callback to event loop");
1278 goto dead;
1279 }
1280
1281 s->blocked = true;
1282 }
1283
1284 s->pending = cd;
1285 return;
1286 }
1287
1288 PERROR("Failed writing to socket %s", s->listen->name);
1289
1290 switch (errno) {
1291 /*
1292 * As a special hack, check for something
1293 * that will never be returned from a
1294 * real write() routine. Which then
1295 * signals to us that we have to close
1296 * the socket, but NOT complain about it.
1297 */
1298 case ECONNREFUSED:
1299 case ECONNRESET:
1300 break;
1301
1302 /*
1303 * These are temporary errors. We just discard the data.
1304 */
1305 case ENETDOWN:
1306 case ENETUNREACH:
1307 if (li->app_io->error) li->app_io->error(li);
1308
1309 fr_message_done(&cd->m);
1310 return;
1311
1312 default:
1313 if (li->app_io->error) li->app_io->error(li);
1314 break;
1315 }
1316
1317 dead:
1318 fr_message_done(&cd->m);
1320 return;
1321 }
1322
1323 /*
1324 * If we've done a partial write, localize the message and continue.
1325 */
1326 if ((size_t) rcode < cd->m.data_size) {
1327 s->written = rcode;
1328 goto save_pending;
1329 }
1330
1331 discard:
1332 s->written = 0;
1333
1334 /*
1335 * Reset for the next message.
1336 */
1337 fr_message_done(&cd->m);
1338 nr->stats.out++;
1339 s->stats.out++;
1340
1341 /*
1342 * Grab the net entry.
1343 */
1344 fr_heap_pop((void **)&cd, &s->waiting);
1345 }
1346
1347 /*
1348 * We've successfully written all of the packets. Remove
1349 * the write callback.
1350 */
1352 PERROR("Failed removing write callback from event loop");
1354 }
1355
1356 s->blocked = false;
1357}
1358
1359/** Stop a socket's I/O events and close its descriptor
1360 *
1361 * Separate from freeing the socket because freeing it also frees s->ms, and
1362 * the workers hold messages allocated from that until they ack the channel
1363 * close. The descriptor can go as soon as we are done with it; the memory
1364 * cannot.
1365 *
1366 * Idempotent, so it is safe to call on a socket that is already dead.
1367 *
1368 * @param[in] s to close.
1369 * @return
1370 * - 0 on success.
1371 * - -1 if the I/O event could not be removed. The descriptor is closed
1372 * either way, so this is worth reporting but not worth stopping for.
1373 */
1375{
1376 int ret = 0;
1377
1378 if (s->closed) return 0;
1379 s->closed = true;
1380
1381 /*
1382 * NULL if the socket never made it into the event loop, which the
1383 * setup error paths rely on. This is the only place the event is
1384 * removed, so the handle cannot have gone stale under us.
1385 */
1386 if (s->ef) {
1387 ret = fr_event_fd_delete_handle(s->ef);
1388 s->ef = NULL;
1389 }
1390
1391 if (s->listen->app_io->close) {
1392 s->listen->app_io->close(s->listen);
1393 } else {
1394 close(s->listen->fd);
1395 }
1396
1397 return ret;
1398}
1399
1401{
1402 fr_network_t *nr = s->nr;
1404
1405 /*
1406 * Closing is a separate step, so that the descriptor can be
1407 * released while the message set stays alive for the workers.
1408 */
1409 fr_assert_msg(s->closed, "socket %d freed without being closed", s->number);
1410 fr_assert(s->outstanding == 0 || nr->exiting);
1411
1412 fr_rb_delete(nr->sockets, s);
1414
1415 if (s->pending) {
1417 s->pending = NULL;
1418 }
1419
1420 /*
1421 * Clean up any queued entries.
1422 */
1423 while ((fr_heap_pop((void **)&cd, &s->waiting) == 0) && cd) {
1424 fr_message_done(&cd->m);
1425 }
1426
1427 /* s->waiting is already talloc parented from s */
1428 talloc_free(s->listen);
1429
1430 return 0;
1431}
1432
1433
1434/** Handle a network control message callback for a new listener
1435 *
1436 * @param[in] data the message
1437 * @param[in] data_size size of the data
1438 * @param[in] now the current time
1439 * @param[in] uctx the network
1440 */
1441static void fr_network_listen_callback(void const *data, size_t data_size, UNUSED fr_time_t now, void *uctx)
1442{
1443 fr_network_t *nr = talloc_get_type_abort(uctx, fr_network_t);
1444 fr_listen_t *li;
1445
1446 fr_assert(data_size == sizeof(li));
1447
1448 if (data_size != sizeof(li)) return;
1449
1450 li = talloc_get_type_abort(*((void * const *)data), fr_listen_t);
1451
1452 (void) fr_network_listen_add_self(nr, li);
1453}
1454
1455static void fr_network_limit_ringbuffer(fr_network_socket_t *s, int *num_messages_p, size_t *size_p)
1456{
1457 int num_messages;
1458 size_t size;
1459
1460 num_messages = s->listen->num_messages;
1461 if (num_messages < 8) num_messages = 8;
1462 if (num_messages > (1 << 20)) num_messages = (1 << 20);
1463
1464 size = s->listen->default_message_size * num_messages;
1465 if (size < (1 << 17)) size = (1 << 17);
1466 if (size > (100 * 1024 * 1024)) size = (100 * 1024 * 1024);
1467
1468 *num_messages_p = num_messages;
1469 *size_p = size;
1470}
1471
1473{
1475 fr_app_io_t const *app_io;
1476 size_t size;
1477 int num_messages;
1478
1479 fr_assert(li->app_io != NULL);
1480
1481 /*
1482 * Non-socket listeners just get told about the event
1483 * list, and nothing else.
1484 */
1485 if (li->non_socket_listener) {
1486 fr_assert(li->app_io->event_list_set != NULL);
1487 fr_assert(!li->app_io->read);
1488 fr_assert(!li->app_io->write);
1489
1490 li->app_io->event_list_set(li, nr->el, nr);
1491
1492 /*
1493 * We use fr_log() here to avoid the "Network - " prefix.
1494 */
1495 fr_log(nr->log, L_DBG, __FILE__, __LINE__, "Listener %s bound to virtual server %s",
1496 li->name, cf_section_name2(li->server_cs));
1497
1498 return 0;
1499 }
1500
1501 s = talloc_zero(nr, fr_network_socket_t);
1502 fr_assert(s != NULL);
1503 talloc_steal(s, li);
1504
1505 s->nr = nr;
1506 s->listen = li;
1507 s->number = nr->num_sockets++;
1508
1509 MEM(s->waiting = fr_heap_alloc(s, waiting_cmp, fr_channel_data_t, channel.heap_id, 0));
1510
1511 talloc_set_destructor(s, _network_socket_free);
1512
1513 /*
1514 * Put reasonable limits on the ring buffer size. Then
1515 * round it up to the nearest power of 2, which is
1516 * required by the ring buffer code.
1517 */
1518 fr_network_limit_ringbuffer(s, &num_messages, &size);
1519
1520 /*
1521 * Allocate the ring buffer for messages and packets.
1522 */
1523 s->ms = fr_message_set_create(s, num_messages,
1524 sizeof(fr_channel_data_t),
1525 size, false);
1526 if (!s->ms) {
1527 PERROR("Failed creating message buffers for network IO");
1528 if (network_socket_close(s) < 0) PWARN("Failed removing event for socket %d", s->number);
1529 talloc_free(s);
1530 return -1;
1531 }
1532
1533 app_io = s->listen->app_io;
1535
1536 if (fr_event_fd_insert(s, &s->ef, nr->el, s->listen->fd,
1540 s) < 0) {
1541 PERROR("Failed adding new socket to network event loop");
1542 if (network_socket_close(s) < 0) PWARN("Failed removing event for socket %d", s->number);
1543 talloc_free(s);
1544 return -1;
1545 }
1546
1547 /*
1548 * Start of with write updates being paused. We don't
1549 * care about being able to write if there's nothing to
1550 * write.
1551 */
1553
1554 /*
1555 * Add the listener before calling the app_io, so that
1556 * the app_io can find the listener which we're adding
1557 * here.
1558 */
1559 (void) fr_rb_insert(nr->sockets, s);
1560 (void) fr_rb_insert(nr->sockets_by_num, s);
1561
1562 if (app_io->event_list_set) app_io->event_list_set(s->listen, nr->el, nr);
1563
1564 /*
1565 * We use fr_log() here to avoid the "Network - " prefix.
1566 */
1567 fr_log(nr->log, L_DBG, __FILE__, __LINE__, "Listening on %s bound to virtual server %s",
1569
1570 DEBUG3("Using new socket %s with FD %d", s->listen->name, s->listen->fd);
1571
1572 return 0;
1573}
1574
1575/** Handle a network control message callback for a new "watch directory"
1576 *
1577 * @param[in] data the message
1578 * @param[in] data_size size of the data
1579 * @param[in] now the current time
1580 * @param[in] uctx the network
1581 */
1582static void fr_network_directory_callback(void const *data, size_t data_size, UNUSED fr_time_t now, void *uctx)
1583{
1584 int num_messages;
1585 size_t size;
1586 fr_network_t *nr = talloc_get_type_abort(uctx, fr_network_t);
1587 fr_listen_t *li = talloc_get_type_abort(*((void * const *)data), fr_listen_t);
1589 fr_app_io_t const *app_io;
1591
1592 if (!fr_cond_assert(data_size == sizeof(li))) return;
1593
1594 memcpy(&li, data, sizeof(li));
1595
1596 s = talloc_zero(nr, fr_network_socket_t);
1597 fr_assert(s != NULL);
1598 talloc_steal(s, li);
1599
1600 s->nr = nr;
1601 s->listen = li;
1602 s->number = nr->num_sockets++;
1603
1604 MEM(s->waiting = fr_heap_alloc(s, waiting_cmp, fr_channel_data_t, channel.heap_id, 0));
1605
1606 talloc_set_destructor(s, _network_socket_free);
1607
1608 /*
1609 * Allocate the ring buffer for messages and packets.
1610 */
1611 fr_network_limit_ringbuffer(s, &num_messages, &size);
1612
1613 s->ms = fr_message_set_create(s, num_messages,
1614 sizeof(fr_channel_data_t),
1615 size, false);
1616 if (!s->ms) {
1617 PERROR("Failed creating message buffers for directory IO");
1618 if (network_socket_close(s) < 0) PWARN("Failed removing event for socket %d", s->number);
1619 talloc_free(s);
1620 return;
1621 }
1622
1623 app_io = s->listen->app_io;
1624
1625 if (app_io->event_list_set) app_io->event_list_set(s->listen, nr->el, nr);
1626
1628
1629 if (fr_event_filter_insert(s, &s->ef, nr->el, s->listen->fd, s->filter,
1630 &funcs,
1631 app_io->error ? fr_network_error : NULL,
1632 s) < 0) {
1633 PERROR("Failed adding directory monitor event loop");
1634 if (network_socket_close(s) < 0) PWARN("Failed removing event for socket %d", s->number);
1635 talloc_free(s);
1636 return;
1637 }
1638
1639 (void) fr_rb_insert(nr->sockets, s);
1640 (void) fr_rb_insert(nr->sockets_by_num, s);
1641
1642 DEBUG3("Using new socket with FD %d", s->listen->fd);
1643}
1644
1645/** Handle a network control message callback for a new worker
1646 *
1647 * @param[in] data the message
1648 * @param[in] data_size size of the data
1649 * @param[in] now the current time
1650 * @param[in] uctx the network
1651 */
1652static void fr_network_worker_started_callback(void const *data, size_t data_size, UNUSED fr_time_t now, void *uctx)
1653{
1654 int i;
1655 fr_network_t *nr = uctx;
1656 fr_worker_t *worker;
1658 fr_worker_config_t const *worker_config;
1659
1660 fr_assert(data_size == sizeof(worker));
1661
1662 if (nr->num_workers >= nr->max_workers) {
1663 ERROR("Too many workers");
1664 return;
1665 }
1666
1667 memcpy(&worker, data, data_size);
1668 (void) talloc_get_type_abort(worker, fr_worker_t);
1669 worker_config = fr_worker_config(worker);
1670
1671 MEM(w = talloc_zero(nr, fr_network_worker_t));
1672
1673 w->worker = worker;
1674 w->reply_ms = fr_message_set_create(w, worker_config->message_set_size, sizeof(fr_channel_data_t),
1675 worker_config->ring_buffer_size, false);
1676 fr_fatal_assert_msg(w->reply_ms, "Failed creating reply message set");
1677 w->channel = fr_worker_channel_create(worker, w, nr->control, w->reply_ms);
1679 fr_fatal_assert_msg(w->channel, "Failed creating new channel");
1680
1683
1684 /*
1685 * Insert the worker into the array of workers.
1686 */
1687 for (i = 0; i < nr->max_workers; i++) {
1688 if (nr->workers[i]) continue;
1689
1690 nr->workers[i] = w;
1691 nr->num_workers++;
1692 return;
1693 }
1694}
1695
1696/** Handle a network control message callback for a packet sent to a socket
1697 *
1698 * @param[in] data the message
1699 * @param[in] data_size size of the data
1700 * @param[in] now the current time
1701 * @param[in] uctx the network
1702 */
1703static void fr_network_inject_callback(void const *data, size_t data_size, UNUSED fr_time_t now, void *uctx)
1704{
1705 fr_network_t *nr = uctx;
1706 fr_network_inject_t my_inject;
1708
1709 fr_assert(data_size == sizeof(my_inject));
1710
1711 memcpy(&my_inject, data, data_size);
1712 fr_rb_find((void **)&s, nr->sockets, &(fr_network_socket_t){ .listen = my_inject.listen });
1713 if (!s) {
1714 talloc_free(my_inject.packet); /* MUST be it's own TALLOC_CTX */
1715 return;
1716 }
1717
1718 /*
1719 * Inject the packet, and then read it back from the
1720 * network.
1721 */
1722 if (s->listen->app_io->inject(s->listen, my_inject.packet, my_inject.packet_len, my_inject.recv_time) == 0) {
1723 fr_network_read(nr->el, s->listen->fd, 0, s);
1724 }
1725
1726 talloc_free(my_inject.packet);
1727}
1728
1729/** Run the event loop 'pre' callback
1730 *
1731 * This function MUST DO NO WORK. All it does is check if there's
1732 * work, and tell the event code to return to the main loop if
1733 * there's work to do.
1734 *
1735 * @param[in] now the current time.
1736 * @param[in] wake the time when the event loop will wake up.
1737 * @param[in] uctx the network
1738 */
1740{
1741 fr_network_t *nr = talloc_get_type_abort(uctx, fr_network_t);
1742
1743 if (fr_heap_num_elements(nr->replies) > 0) return 1;
1744
1745 return 0;
1746}
1747
1748/** Handle replies after all FD and timer events have been serviced
1749 *
1750 * @param el the event loop
1751 * @param now the current time (mostly)
1752 * @param uctx the fr_network_t
1753 */
1755{
1757 fr_network_t *nr = talloc_get_type_abort(uctx, fr_network_t);
1758
1759 /*
1760 * Pull the replies off of our global heap, and try to
1761 * push them to the individual sockets.
1762 */
1763 while ((fr_heap_pop((void **)&cd, &nr->replies) == 0) && cd) {
1764 fr_listen_t *li;
1766
1767 li = cd->listen;
1768
1769 /*
1770 * @todo - cache this somewhere so we don't need
1771 * to do an rbtree lookup for every packet.
1772 */
1773 fr_rb_find((void **)&s, nr->sockets, &(fr_network_socket_t){ .listen = li });
1774
1775 /*
1776 * This shouldn't happen, but be safe...
1777 */
1778 if (!s) {
1779 fr_message_done(&cd->m);
1780 continue;
1781 }
1782
1783 if (cd->m.status != FR_MESSAGE_LOCALIZED) {
1784 fr_assert(s->outstanding > 0);
1785 s->outstanding--;
1786 }
1787
1788 /*
1789 * Just mark the message done, and skip it.
1790 */
1791 if (s->dead) {
1792 fr_message_done(&cd->m);
1793
1794 /*
1795 * No more packets, it's safe to delete
1796 * the socket.
1797 */
1798 if (!s->outstanding) talloc_free(s);
1799
1800 continue;
1801 }
1802
1803 /*
1804 * No data to write to the socket, so we skip the message.
1805 */
1806 if (!cd->m.data_size) {
1807 fr_message_done(&cd->m);
1808 continue;
1809 }
1810
1811 (void) fr_heap_insert(&s->waiting, cd);
1812
1813 /*
1814 * No pending message, write it. If there is a pending write, the message will be left
1815 * in the waiting queue.
1816 */
1817 if (!s->pending) {
1818 fr_assert(!s->blocked);
1819 fr_network_write(nr->el, s->listen->fd, 0, s);
1820 }
1821 }
1822}
1823
1825{
1826 fr_network_t *nr = talloc_get_type_abort(uctx, fr_network_t);
1827
1828 return (nr->num_workers == 0) ? 1 : 0;
1829}
1830
1832{
1833 fr_network_t *nr = talloc_get_type_abort(uctx, fr_network_t);
1834
1835 if (nr->num_workers == 0) fr_event_loop_exit(el, SIGINT);
1836}
1837
1838/** Add events to the loop which will exit when the number of workers has reached zero
1839 */
1841{
1843 fr_strerror_const("Failed adding close pre-event to event list");
1844 return -1;
1845 }
1847 fr_strerror_const("Failed adding close post-event to event list");
1848 return -1;
1849 }
1850 return 0;
1851}
1852
1853/** Remove "close" events from the event loop.
1854 */
1860
1861/** Stop a network thread in an orderly way
1862 *
1863 * @param[in] nr the network to stop
1864 */
1866{
1868 int ret = 0;
1869
1870 (void) talloc_get_type_abort(nr, fr_network_t);
1871
1872 /*
1873 * Close the network sockets, but leave them allocated.
1874 *
1875 * Freeing a socket frees its message set, and the workers hold
1876 * messages allocated from it until they ack the channel close
1877 * signalled below. fr_network() frees them after its loop, which
1878 * already runs until every worker has acked.
1879 */
1880 {
1881 fr_network_socket_t **sockets;
1882 size_t len;
1883 size_t i;
1884
1885 if (fr_rb_flatten_inorder(nr, (void ***)&sockets, nr->sockets) < 0) return -1;
1886 len = talloc_array_length(sockets);
1887
1888 for (i = 0; i < len; i++) {
1889 if (network_socket_close(sockets[i]) < 0) {
1890 PWARN("Failed removing event for socket %d", sockets[i]->number);
1891 ret = -1;
1892 }
1893 }
1894
1895 talloc_free(sockets);
1896 }
1897
1898
1899 /*
1900 * Clean up all outstanding replies.
1901 *
1902 * We can't do this after signalling the
1903 * workers to close, because they free
1904 * their message sets, and we end up
1905 * getting random use-after-free errors
1906 * as there's a race between the network
1907 * popping replies, and the workers
1908 * freeing their message sets.
1909 *
1910 * This isn't perfect, and we might still
1911 * lose some replies, but it's good enough
1912 * for now.
1913 *
1914 * @todo - call transport "done" for the reply, so that
1915 * it knows the replies are done, too.
1916 */
1917 while ((fr_heap_pop((void **)&cd, &nr->replies) == 0) && cd) {
1918 fr_message_done(&cd->m);
1919 }
1920
1921 /*
1922 * Nothing may be queued from here on: a worker that has been
1923 * signalled discards whatever arrives late, without a reply.
1924 */
1925 nr->exiting = true;
1926
1927 /*
1928 * Signal the workers that we're closing
1929 *
1930 * nr->num_workers is decremented every
1931 * time a worker closes a socket.
1932 *
1933 * When nr->num_workers == 0, the event
1934 * loop (fr_network()) will exit.
1935 */
1936 {
1937 int i;
1938
1939 for (i = 0; i < nr->num_workers; i++) {
1940 fr_network_worker_t *worker = nr->workers[i];
1941
1943 }
1944 }
1945
1949
1950 return ret;
1951}
1952
1953/** Read handler for signal pipe
1954 *
1955 */
1956static void _signal_pipe_read(UNUSED fr_event_list_t *el, int fd, UNUSED int flags, void *uctx)
1957{
1958 fr_network_t *nr = talloc_get_type_abort(uctx, fr_network_t);
1959 uint8_t buff;
1960
1961 if (read(fd, &buff, sizeof(buff)) < 0) {
1962 ERROR("Failed reading signal - %s", fr_syserror(errno));
1963 return;
1964 }
1965
1966 fr_assert(buff == 1);
1967
1968 /*
1969 * fr_network_stop() will signal the workers
1970 * to exit (by closing their channels).
1971 *
1972 * When we get the ack, we decrement our
1973 * nr->num_workers counter.
1974 *
1975 * When the counter reaches 0, the event loop
1976 * exits.
1977 */
1978 DEBUG2("Signalled to exit");
1979
1980 if (unlikely(fr_network_destroy(nr) < 0)) {
1981 PERROR("Failed stopping network");
1982 }
1983}
1984
1985/** The main network worker function.
1986 *
1987 * @param[in] nr the network data structure to run.
1988 */
1990{
1991 /*
1992 * Run until we're told to exit AND the number of
1993 * workers has dropped to zero.
1994 *
1995 * This is important as if we exit too early we
1996 * free the channels out from underneath the
1997 * workers and they read uninitialised memory.
1998 *
1999 * Whenever a worker ACKs our close notification
2000 * nr->num_workers is decremented, so when
2001 * nr->num_workers == 0, all workers have ACKd
2002 * our close and are no longer using the channel.
2003 */
2004 while (likely(!(nr->exiting && (nr->num_workers == 0)))) {
2005 bool wait_for_event;
2006 int num_events;
2007
2008 /*
2009 * There are runnable requests. We still service
2010 * the event loop, but we don't wait for events.
2011 */
2012 wait_for_event = (fr_heap_num_elements(nr->replies) == 0);
2013
2014 /*
2015 * Check the event list. If there's an error
2016 * (e.g. exit), we stop looping and clean up.
2017 */
2018 DEBUG4("Gathering events - %s", wait_for_event ? "will wait" : "Will not wait");
2019 num_events = fr_event_corral(nr->el, fr_time(), wait_for_event);
2020 DEBUG4("%u event(s) pending%s",
2021 num_events == -1 ? 0 : num_events, num_events == -1 ? " - event loop exiting" : "");
2022 if (num_events < 0) break;
2023
2024 /*
2025 * Service outstanding events.
2026 */
2027 if (num_events > 0) {
2028 DEBUG4("Servicing event(s)");
2029 fr_event_service(nr->el);
2030 }
2031 }
2032
2033 /*
2034 * Free the sockets. The loop above only exits once every worker
2035 * has acked, so the messages allocated from the sockets' message
2036 * sets have all been returned, and fr_network_destroy() left the
2037 * sockets closed but allocated for exactly this point. It cannot do
2038 * this itself, as it returns long before the first ack arrives. The
2039 * error exit above lands here too, where no ack is ever coming.
2040 */
2041 {
2042 fr_network_socket_t **sockets;
2043 size_t len;
2044 size_t i;
2045
2046 if (fr_rb_flatten_inorder(nr, (void ***)&sockets, nr->sockets) < 0) {
2047 PWARN("Failed enumerating sockets, leaving them to be freed with the network");
2048 return;
2049 }
2050 len = talloc_array_length(sockets);
2051
2052 for (i = 0; i < len; i++) talloc_free(sockets[i]);
2053
2054 talloc_free(sockets);
2055 }
2056}
2057
2058/** Signal a network thread to exit
2059 *
2060 * @note Request to exit will be processed asynchronously.
2061 *
2062 * @param[in] nr the network data structure to manage
2063 * @return
2064 * - 0 on success.
2065 * - -1 on failure.
2066 */
2068{
2069 if (write(nr->signal_pipe[1], &(uint8_t){ 0x01 }, 1) < 0) {
2070 fr_strerror_printf("Failed signalling network thread to exit - %s", fr_syserror(errno));
2071 return -1;
2072 }
2073
2074 return 0;
2075}
2076
2077/** Free any resources associated with a network thread
2078 *
2079 */
2081{
2082 if (nr->signal_pipe[0] >= 0) close(nr->signal_pipe[0]);
2083 if (nr->signal_pipe[1] >= 0) close(nr->signal_pipe[1]);
2084
2085 return 0;
2086}
2087
2088/** Create a network
2089 *
2090 * @param[in] ctx The talloc ctx
2091 * @param[in] el The event list
2092 * @param[in] name Networker identifier.
2093 * @param[in] logger The destination for all logging messages
2094 * @param[in] lvl Log level
2095 * @param[in] config configuration structure.
2096 * @return
2097 * - NULL on error
2098 * - fr_network_t on success
2099 */
2100fr_network_t *fr_network_create(TALLOC_CTX *ctx, fr_event_list_t *el, char const *name,
2101 fr_log_t const *logger, fr_log_lvl_t lvl,
2103{
2104 fr_network_t *nr;
2105
2106 nr = talloc_zero(ctx, fr_network_t);
2107 if (!nr) {
2108 fr_strerror_const("Failed allocating memory");
2109 return NULL;
2110 }
2111 talloc_set_destructor(nr, _fr_network_free);
2112
2113 nr->name = talloc_strdup(nr, name);
2114
2115 nr->thread_id = pthread_self();
2116 nr->el = el;
2117 nr->log = logger;
2118 nr->lvl = lvl;
2119
2121 nr->num_workers = 0;
2122 nr->signal_pipe[0] = -1;
2123 nr->signal_pipe[1] = -1;
2124 if (config) nr->config = *config;
2125
2126 nr->aq_control = fr_atomic_queue_talloc(nr, 1024);
2127 if (!nr->aq_control) {
2128 talloc_free(nr);
2129 return NULL;
2130 }
2131
2132 nr->control = fr_control_create(nr, el, nr->aq_control, 6);
2133 if (!nr->control) {
2134 fr_strerror_const_push("Failed creating control queue");
2135 fail:
2136 talloc_free(nr);
2137 return NULL;
2138 }
2139
2140 /*
2141 * @todo - rely on thread-local variables. And then the
2142 * various users of this can check if (rb == nr->rb), and
2143 * if so, skip the whole control plane / kevent /
2144 * whatever roundabout thing.
2145 */
2147 if (!nr->rb) {
2148 fr_strerror_const_push("Failed creating ring buffer");
2149 fail2:
2150 talloc_free(nr->control);
2151 goto fail;
2152 }
2153
2155 fr_strerror_const_push("Failed adding channel callback");
2156 goto fail2;
2157 }
2158
2160 fr_strerror_const_push("Failed adding socket callback");
2161 goto fail2;
2162 }
2163
2165 fr_strerror_const_push("Failed adding socket callback");
2166 goto fail2;
2167 }
2168
2170 fr_strerror_const_push("Failed adding worker callback");
2171 goto fail2;
2172 }
2173
2175 fr_strerror_const_push("Failed adding packet injection callback");
2176 goto fail2;
2177 }
2178
2179 if (fr_control_open(nr->control) < 0) {
2180 fr_strerror_const_push("Failed opening control queue");
2181 goto fail2;
2182 }
2183
2184 /*
2185 * Create the various heaps.
2186 */
2188 if (!nr->sockets) {
2189 fr_strerror_const_push("Failed creating listen tree for sockets");
2190 goto fail2;
2191 }
2192
2194 if (!nr->sockets_by_num) {
2195 fr_strerror_const_push("Failed creating number tree for sockets");
2196 goto fail2;
2197 }
2198
2199 nr->replies = fr_heap_alloc(nr, reply_cmp, fr_channel_data_t, channel.heap_id, 0);
2200 if (!nr->replies) {
2201 fr_strerror_const_push("Failed creating heap for replies");
2202 goto fail2;
2203 }
2204
2205 if (fr_event_pre_insert(nr->el, fr_network_pre_event, nr) < 0) {
2206 fr_strerror_const("Failed adding pre-check to event list");
2207 goto fail2;
2208 }
2209
2210 if (fr_event_post_insert(nr->el, fr_network_post_event, nr) < 0) {
2211 fr_strerror_const("Failed inserting post-processing event");
2212 goto fail2;
2213 }
2214
2215 if (pipe(nr->signal_pipe) < 0) {
2216 fr_strerror_printf("Failed initialising signal pipe - %s", fr_syserror(errno));
2217 goto fail2;
2218 }
2219 if (fr_nonblock(nr->signal_pipe[0]) < 0) goto fail2;
2220 if (fr_nonblock(nr->signal_pipe[1]) < 0) goto fail2;
2221
2222 if (fr_event_fd_insert(nr, NULL, nr->el, nr->signal_pipe[0], _signal_pipe_read, NULL, NULL, nr) < 0) {
2223 fr_strerror_const("Failed inserting event for signal pipe");
2224 goto fail2;
2225 }
2226
2227 return nr;
2228}
2229
2230int fr_network_stats(fr_network_t const *nr, int num, uint64_t *stats)
2231{
2232 if (num < 0) return -1;
2233 if (num == 0) return 0;
2234
2235 stats[0] = nr->stats.in;
2236 if (num >= 2) stats[1] = nr->stats.out;
2237 if (num >= 3) stats[2] = nr->stats.dup;
2238 if (num >= 4) stats[3] = nr->stats.dropped;
2239 if (num >= 5) stats[4] = nr->num_workers;
2240
2241 if (num <= 5) return num;
2242
2243 return 5;
2244}
2245
2246void fr_network_stats_log(fr_network_t const *nr, fr_log_t const *log)
2247{
2248 int i;
2249
2250 /*
2251 * Dump all of the channel statistics.
2252 */
2253 for (i = 0; i < nr->max_workers; i++) {
2254 if (!nr->workers[i]) continue;
2255
2256 fr_channel_stats_log(nr->workers[i]->channel, log, __FILE__, __LINE__);
2257 }
2258}
2259
2260static int cmd_stats_self(FILE *fp, UNUSED FILE *fp_err, void *ctx, UNUSED fr_cmd_info_t const *info)
2261{
2262 fr_network_t const *nr = ctx;
2263
2264 fprintf(fp, "count.in\t%" PRIu64 "\n", nr->stats.in);
2265 fprintf(fp, "count.out\t%" PRIu64 "\n", nr->stats.out);
2266 fprintf(fp, "count.dup\t%" PRIu64 "\n", nr->stats.dup);
2267 fprintf(fp, "count.dropped\t%" PRIu64 "\n", nr->stats.dropped);
2268 fprintf(fp, "count.sockets\t%u\n", fr_rb_num_elements(nr->sockets));
2269
2270 return 0;
2271}
2272
2273static int cmd_socket_list(FILE *fp, UNUSED FILE *fp_err, void *ctx, UNUSED fr_cmd_info_t const *info)
2274{
2275 fr_network_t const *nr = ctx;
2278
2279 // @todo - note that this isn't thread-safe!
2280
2281 for (s = fr_rb_iter_init_inorder(nr->sockets, &iter);
2282 s != NULL;
2283 s = fr_rb_iter_next_inorder(nr->sockets, &iter)) {
2284 if (!s->listen->app_io->get_name) {
2285 fprintf(fp, "%s\n", s->listen->app_io->common.name);
2286 } else {
2287 fprintf(fp, "%d\t%s\n", s->number, s->listen->app_io->get_name(s->listen));
2288 }
2289 }
2290 return 0;
2291}
2292
2293static int cmd_stats_socket(FILE *fp, FILE *fp_err, void *ctx, fr_cmd_info_t const *info)
2294{
2295 fr_network_t const *nr = ctx;
2297
2298 fr_rb_find((void **)&s, nr->sockets_by_num, &(fr_network_socket_t){ .number = info->box[0]->vb_uint32 });
2299 if (!s) {
2300 fprintf(fp_err, "No such socket number '%s'.\n", info->argv[0]);
2301 return -1;
2302 }
2303
2304 fprintf(fp, "count.in\t%" PRIu64 "\n", s->stats.in);
2305 fprintf(fp, "count.out\t%" PRIu64 "\n", s->stats.out);
2306 fprintf(fp, "count.dup\t%" PRIu64 "\n", s->stats.dup);
2307 fprintf(fp, "count.dropped\t%" PRIu64 "\n", s->stats.dropped);
2308
2309 return 0;
2310}
2311
2312
2314 {
2315 .parent = "stats",
2316 .name = "network",
2317 .help = "Statistics for network threads.",
2318 .read_only = true
2319 },
2320
2321 {
2322 .parent = "stats network",
2323 .add_name = true,
2324 .name = "self",
2325 .func = cmd_stats_self,
2326 .help = "Show statistics for a specific network thread.",
2327 .read_only = true
2328 },
2329
2330 {
2331 .parent = "stats network",
2332 .add_name = true,
2333 .name = "socket",
2334 .syntax = "INTEGER",
2335 .func = cmd_stats_socket,
2336 .help = "Show statistics for a specific socket",
2337 .read_only = true
2338 },
2339
2340 {
2341 .parent = "show",
2342 .name = "network",
2343 .help = "Show information about network threads.",
2344 .read_only = true
2345 },
2346
2347 {
2348 .parent = "show network",
2349 .add_name = true,
2350 .name = "socket",
2351 .syntax = "list",
2352 .func = cmd_socket_list,
2353 .help = "List the sockets associated with this network thread.",
2354 .read_only = true
2355 },
2356
2358};
static int const char char buffer[256]
Definition acutest.h:635
fr_io_close_t close
Close the transport.
Definition app_io.h:60
fr_io_data_read_t read
Read from a socket to a data buffer.
Definition app_io.h:47
module_t common
Common fields to all loadable modules.
Definition app_io.h:34
fr_io_signal_t error
There was an error on the socket.
Definition app_io.h:59
fr_app_event_list_set_t event_list_set
Called by the network thread to pass an event list for use by the app_io_t.
Definition app_io.h:36
fr_io_data_inject_t inject
Inject a packet into a socket.
Definition app_io.h:50
fr_io_data_vnode_t vnode
Handle notifications that the VNODE has changed.
Definition app_io.h:52
fr_io_data_write_t write
Write from a data buffer to a socket.
Definition app_io.h:48
fr_io_name_t get_name
get the socket name
Definition app_io.h:70
Public structure describing an I/O path for a protocol.
Definition app_io.h:33
fr_app_priority_get_t priority
Assign a priority to the packet.
Definition application.h:90
#define _Thread_local
Definition atexit.h:213
#define fr_atexit_thread_local(_name, _free, _uctx)
Definition atexit.h:224
fr_atomic_queue_t * fr_atomic_queue_talloc(TALLOC_CTX *ctx, size_t size)
Create fixed-size atomic queue.
Structure to hold the atomic queue.
static int packet_ctx
the tests pass only the address of packet_ctx
#define RCSID(id)
Definition build.h:560
#define CMP(_a, _b)
Same as CMP_PREFER_SMALLER use when you don't really care about ordering, you just want an ordering.
Definition build.h:113
#define unlikely(_x)
Definition build.h:455
#define UNUSED
Definition build.h:384
char const * cf_section_name2(CONF_SECTION const *cs)
Return the second identifier of a CONF_SECTION.
Definition cf_util.c:1393
void * fr_channel_requestor_uctx_get(fr_channel_t *ch)
Get network-specific data from a channel.
Definition channel.c:964
fr_table_num_sorted_t const channel_signals[]
Definition channel.c:152
bool fr_channel_recv_reply(fr_channel_t *ch)
Receive a reply message from the channel.
Definition channel.c:407
int fr_channel_signal_responder_close(fr_channel_t *ch)
Signal a responder that the channel is closing.
Definition channel.c:844
int fr_channel_send_request(fr_channel_t *ch, fr_channel_data_t *cd)
Send a request message into the channel.
Definition channel.c:305
int fr_channel_set_recv_reply(fr_channel_t *ch, fr_channel_recv_callback_t recv_reply, void *uctx)
Definition channel.c:972
void fr_channel_requestor_uctx_add(fr_channel_t *ch, void *uctx)
Add network-specific data to a channel.
Definition channel.c:952
void fr_channel_stats_log(fr_channel_t const *ch, fr_log_t const *log, char const *file, int line)
Definition channel.c:1006
fr_channel_event_t fr_channel_service_message(fr_time_t when, fr_channel_t **p_channel, void **uctx_out, void const *data, size_t data_size)
Service a control-plane message.
Definition channel.c:689
A full channel, which consists of two ends.
Definition channel.c:143
fr_message_t m
the message header
Definition channel.h:107
fr_channel_event_t
Definition channel.h:69
@ FR_CHANNEL_NOOP
Definition channel.h:76
@ FR_CHANNEL_EMPTY
Definition channel.h:77
@ FR_CHANNEL_CLOSE
Definition channel.h:74
@ FR_CHANNEL_ERROR
Definition channel.h:70
@ FR_CHANNEL_DATA_READY_REQUESTOR
Definition channel.h:72
@ FR_CHANNEL_OPEN
Definition channel.h:73
@ FR_CHANNEL_DATA_READY_RESPONDER
Definition channel.h:71
void * packet_ctx
Packet specific context for holding client information, and other proto_* specific information that n...
Definition channel.h:144
fr_listen_t * listen
for tracking packet transport, etc.
Definition channel.h:148
#define FR_CONTROL_ID_CHANNEL
Definition channel.h:67
#define PRIORITY_NORMAL
Definition channel.h:153
#define PRIORITY_NOW
Definition channel.h:151
uint32_t priority
Priority of this packet.
Definition channel.h:142
Channel information which is added to a message.
Definition channel.h:106
char const * parent
e.g. "show module"
Definition command.h:52
#define CMD_TABLE_END
Definition command.h:62
char const ** argv
text version of commands
Definition command.h:42
#define FR_CONTROL_MAX_SIZE
Definition control.h:51
#define FR_CONTROL_MAX_MESSAGES
Definition control.h:50
#define fr_cond_assert(_x)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
Definition debug.h:177
#define fr_assert_msg(_x, _msg,...)
Calls panic_action ifndef NDEBUG, else logs error and causes the server to exit immediately with code...
Definition debug.h:248
#define fr_cond_assert_msg(_x, _fmt,...)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
Definition debug.h:194
#define fr_fatal_assert_msg(_x, _fmt,...)
Calls panic_action ifndef NDEBUG, else logs error and causes the server to exit immediately with code...
Definition debug.h:222
#define MEM(x)
Definition debug.h:38
#define ERROR(fmt,...)
Definition dhcpclient.c:40
static int sockfd
Definition dhcpclient.c:55
fr_dict_t const * fr_dict_proto_dict(fr_dict_t const *dict)
Definition dict_util.c:5416
#define fr_event_fd_insert(...)
Definition event.h:247
fr_event_filter_t
The type of filter to install for an FD.
Definition event.h:82
@ FR_EVENT_FILTER_VNODE
Filter for vnode subfilters.
Definition event.h:84
@ FR_EVENT_FILTER_IO
Combined filter for read/write functions/.
Definition event.h:83
#define fr_event_filter_update(...)
Definition event.h:239
#define fr_event_filter_insert(...)
Definition event.h:234
#define FR_EVENT_RESUME(_s, _f)
Re-add the filter for a func from kevent.
Definition event.h:131
#define FR_EVENT_SUSPEND(_s, _f)
Temporarily remove the filter for a func from kevent.
Definition event.h:115
fr_event_fd_cb_t extend
Additional files were added to a directory.
Definition event.h:198
Callbacks for the FR_EVENT_FILTER_IO filter.
Definition event.h:188
Structure describing a modification to a filter's state.
Definition event.h:96
Callbacks for the FR_EVENT_FILTER_VNODE filter.
Definition event.h:195
int fr_heap_insert(fr_heap_t **hp, void *data)
Insert a new element into the heap.
Definition heap.c:149
int fr_heap_pop(void **out, fr_heap_t **hp)
Remove a node from the heap.
Definition heap.c:359
unsigned int fr_heap_index_t
Definition heap.h:82
#define fr_heap_alloc(_ctx, _cmp, _type, _field, _init)
Creates a heap that can be used with non-talloced elements.
Definition heap.h:102
static unsigned int fr_heap_num_elements(fr_heap_t *h)
Return the number of elements in the heap.
Definition heap.h:181
#define FR_HEAP_INDEX_INVALID
Definition heap.h:85
The main heap structure.
Definition heap.h:68
talloc_free(hp)
uint64_t out
Definition base.h:43
uint64_t dup
Definition base.h:44
uint64_t dropped
Definition base.h:45
uint64_t in
Definition base.h:42
fr_control_t * fr_control_create(TALLOC_CTX *ctx, fr_event_list_t *el, fr_atomic_queue_t *aq, size_t num_callbacks)
Create a control-plane signaling path.
Definition control.c:155
int fr_control_open(fr_control_t *c)
Open the control-plane signalling path.
Definition control.c:179
int fr_control_message_send(fr_control_t *c, fr_ring_buffer_t *rb, uint32_t id, void *data, size_t data_size)
Send a control-plane message.
Definition control.c:358
int fr_control_callback_add(fr_control_t **c, uint32_t id, fr_control_callback_t callback, void *uctx)
Register a callback for an ID.
Definition control.c:447
The control structure.
Definition control.c:76
static void fr_network_inject_callback(void const *data, size_t data_size, UNUSED fr_time_t now, void *uctx)
Handle a network control message callback for a packet sent to a socket.
Definition network.c:1703
fr_ring_buffer_t * rb
ring buffer for my control-plane messages
Definition network.c:125
static int fr_network_pre_close_event(UNUSED fr_time_t now, UNUSED fr_time_delta_t wake, void *uctx)
Definition network.c:1824
fr_cmd_table_t cmd_network_table[]
Definition network.c:2313
size_t written
however much we did in a partial write
Definition network.c:91
int fr_network_listen_send_packet(fr_network_t *nr, fr_listen_t *parent, fr_listen_t *li, const uint8_t *buffer, size_t buflen, fr_time_t recv_time, void *packet_ctx)
Send a packet to the worker.
Definition network.c:822
fr_atomic_queue_t * aq_control
atomic queue for control messages sent to me
Definition network.c:121
static int cmd_stats_socket(FILE *fp, FILE *fp_err, void *ctx, fr_cmd_info_t const *info)
Definition network.c:2293
int fr_network_listen_add(fr_network_t *nr, fr_listen_t *li)
Add a fr_listen_t to a network.
Definition network.c:236
bool suspended
whether or not we're suspended.
Definition network.c:116
void fr_network_close_event_delete(fr_network_t *nr)
Remove "close" events from the event loop.
Definition network.c:1855
int fr_network_worker_add(fr_network_t *nr, fr_worker_t *worker)
Add a worker to a network in a different thread.
Definition network.c:305
int fr_network_destroy(fr_network_t *nr)
Stop a network thread in an orderly way.
Definition network.c:1865
fr_network_t * nr
O(N) issues in talloc.
Definition network.c:73
fr_io_stats_t stats
Definition network.c:66
fr_listen_t * listen
Definition network.c:47
static fr_cmp_ret_t socket_num_cmp(void const *one, void const *two)
Definition network.c:186
fr_event_fd_t * ef
the I/O event, if it was inserted.
Definition network.c:78
uint8_t * packet
Definition network.c:48
static int cmd_stats_self(FILE *fp, UNUSED FILE *fp_err, void *ctx, UNUSED fr_cmd_info_t const *info)
Definition network.c:2260
fr_log_t const * log
log destination
Definition network.c:118
int fr_network_directory_add(fr_network_t *nr, fr_listen_t *li)
Add a "watch directory" call to a network.
Definition network.c:290
static int _fr_network_free(fr_network_t *nr)
Free any resources associated with a network thread.
Definition network.c:2080
fr_heap_index_t heap_id
for the sockets_by_num heap
Definition network.c:75
#define RTT(_old, _new)
Definition network.c:518
void fr_network(fr_network_t *nr)
The main network worker function.
Definition network.c:1989
fr_message_set_t * ms
message buffers for this socket.
Definition network.c:88
int fr_network_listen_delete(fr_network_t *nr, fr_listen_t *li)
Delete a socket from a network.
Definition network.c:271
int num_blocked
number of blocked workers
Definition network.c:137
ssize_t fr_network_listen_outstanding(fr_network_t *nr, fr_listen_t *li)
Get the number of outstanding packets.
Definition network.c:864
char const * name
Network ID for logging.
Definition network.c:112
void fr_network_worker_add_self(fr_network_t *nr, fr_worker_t *worker)
Add a worker to a network in the same thread.
Definition network.c:325
unsigned int outstanding
number of outstanding packets sent to the worker
Definition network.c:85
static _Thread_local fr_ring_buffer_t * fr_network_rb
Definition network.c:44
int number
unique ID
Definition network.c:74
static fr_event_update_t const resume_write[]
Definition network.c:476
fr_message_set_t * reply_ms
message set the worker will use to send reply data.
Definition network.c:64
fr_time_delta_t predicted
predicted processing time for one packet
Definition network.c:59
static int fr_network_pre_event(fr_time_t now, fr_time_delta_t wake, void *uctx)
static fr_event_update_t const pause_read[]
Definition network.c:461
fr_worker_t * worker
worker pointer
Definition network.c:65
int fr_network_sendto_worker(fr_network_t *nr, fr_listen_t *li, void *packet_ctx, uint8_t const *data, size_t data_len, fr_time_t recv_time)
Definition network.c:1116
int fr_network_exit(fr_network_t *nr)
Signal a network thread to exit.
Definition network.c:2067
#define MAX_WORKERS
Definition network.c:42
int fr_network_listen_inject(fr_network_t *nr, fr_listen_t *li, uint8_t const *packet, size_t packet_len, fr_time_t recv_time)
Inject a packet for a listener to read.
Definition network.c:410
fr_listen_t * listen
I/O ctx and functions.
Definition network.c:86
int num_sockets
actually a counter...
Definition network.c:140
fr_rb_node_t listen_node
rbtree node for looking up by listener.
Definition network.c:70
static void fr_network_channel_callback(void const *data, size_t data_size, fr_time_t now, void *uctx)
Handle a network control message callback for a channel.
Definition network.c:574
static void fr_network_vnode_extend(UNUSED fr_event_list_t *el, int sockfd, int fflags, void *ctx)
Get a notification that a vnode changed.
Definition network.c:1160
static void _signal_pipe_read(UNUSED fr_event_list_t *el, int fd, UNUSED int flags, void *uctx)
Read handler for signal pipe.
Definition network.c:1956
#define OUTSTANDING(_x)
Definition network.c:635
int num_workers
number of active workers
Definition network.c:136
int num_pending_workers
number of workers we're waiting to start.
Definition network.c:138
fr_rb_tree_t * sockets
list of sockets we're managing, ordered by the listener
Definition network.c:133
bool closed
the descriptor has been closed.
Definition network.c:83
pthread_t thread_id
for self
Definition network.c:114
fr_log_lvl_t lvl
debug log level
Definition network.c:119
int signal_pipe[2]
Pipe for signalling the worker in an orderly way.
Definition network.c:142
fr_channel_data_t * pending
the currently pending partial packet
Definition network.c:93
static void fr_network_write(UNUSED fr_event_list_t *el, UNUSED int sockfd, UNUSED int flags, void *ctx)
Write packets to the network.
Definition network.c:1212
fr_event_list_t * el
our event list
Definition network.c:127
fr_heap_t * replies
replies from the worker, ordered by priority / origin time
Definition network.c:129
static int fr_network_send_request(fr_network_t *nr, fr_channel_data_t *cd)
Send a message on the "best" channel.
Definition network.c:642
static fr_event_update_t const resume_read[]
Definition network.c:466
void fr_network_stats_log(fr_network_t const *nr, fr_log_t const *log)
Definition network.c:2246
fr_heap_t * waiting
packets waiting to be written
Definition network.c:94
int fr_network_stats(fr_network_t const *nr, int num, uint64_t *stats)
Definition network.c:2230
fr_heap_index_t heap_id
workers are in a heap
Definition network.c:57
static void fr_network_worker_started_callback(void const *data, size_t data_size, fr_time_t now, void *uctx)
bool blocked
is this worker blocked?
Definition network.c:61
static void fr_network_read(UNUSED fr_event_list_t *el, int sockfd, UNUSED int flags, void *ctx)
Read a packet from the network.
Definition network.c:925
static bool is_network_thread(fr_network_t const *nr)
Definition network.c:224
static fr_cmp_ret_t reply_cmp(void const *one, void const *two)
Definition network.c:157
fr_rb_node_t num_node
rbtree node for looking up by number.
Definition network.c:71
static void fr_network_error(UNUSED fr_event_list_t *el, UNUSED int sockfd, int flags, int fd_errno, void *ctx)
Handle errors for a socket.
Definition network.c:1185
fr_io_stats_t stats
Definition network.c:95
static fr_ring_buffer_t * fr_network_rb_init(void)
Initialise thread local storage.
Definition network.c:206
fr_channel_data_t * cd
cached in case of allocation & read error
Definition network.c:89
static int fr_network_listen_add_self(fr_network_t *nr, fr_listen_t *listen)
Definition network.c:1472
static void fr_network_suspend(fr_network_t *nr)
Definition network.c:481
bool dead
is it dead?
Definition network.c:81
size_t leftover
leftover data from a previous read
Definition network.c:90
int fr_network_close_event_insert(fr_network_t *nr)
Add events to the loop which will exit when the number of workers has reached zero.
Definition network.c:1840
static void fr_network_post_event(fr_event_list_t *el, fr_time_t now, void *uctx)
fr_network_worker_t * workers[MAX_WORKERS]
each worker
Definition network.c:148
fr_time_t recv_time
Definition network.c:50
static void fr_network_unsuspend(fr_network_t *nr)
Definition network.c:499
fr_rb_tree_t * sockets_by_num
ordered by number;
Definition network.c:134
fr_network_config_t config
configuration
Definition network.c:147
void fr_network_listen_read(fr_network_t *nr, fr_listen_t *li)
Signal the network to read from a listener.
Definition network.c:336
static fr_cmp_ret_t waiting_cmp(void const *one, void const *two)
Definition network.c:168
fr_io_stats_t stats
Definition network.c:131
static void fr_network_limit_ringbuffer(fr_network_socket_t *s, int *num_messages_p, size_t *size_p)
Definition network.c:1455
static int _fr_network_rb_free(void *arg)
Definition network.c:197
int max_workers
maximum number of allowed workers
Definition network.c:139
void fr_network_listen_write(fr_network_t *nr, fr_listen_t *li, uint8_t const *packet, size_t packet_len, void *packet_ctx, fr_time_t request_time)
Inject a packet for a listener to write.
Definition network.c:362
static void fr_network_recv_reply(fr_channel_t *ch, fr_channel_data_t *cd, void *uctx)
Callback which handles a message being received on the network side.
Definition network.c:526
bool exiting
are we exiting?
Definition network.c:145
fr_event_filter_t filter
what type of filter it is
Definition network.c:77
static void fr_network_socket_dead(fr_network_t *nr, fr_network_socket_t *s)
Definition network.c:880
fr_channel_t * channel
channel to the worker
Definition network.c:63
static fr_event_update_t const pause_write[]
Definition network.c:471
fr_network_t * fr_network_create(TALLOC_CTX *ctx, fr_event_list_t *el, char const *name, fr_log_t const *logger, fr_log_lvl_t lvl, fr_network_config_t const *config)
Create a network.
Definition network.c:2100
static int _network_socket_free(fr_network_socket_t *s)
Definition network.c:1400
static void fr_network_directory_callback(void const *data, size_t data_size, UNUSED fr_time_t now, void *uctx)
Handle a network control message callback for a new "watch directory".
Definition network.c:1582
static int network_socket_close(fr_network_socket_t *s)
Stop a socket's I/O events and close its descriptor.
Definition network.c:1374
static fr_cmp_ret_t socket_listen_cmp(void const *one, void const *two)
Definition network.c:179
static int cmd_socket_list(FILE *fp, UNUSED FILE *fp_err, void *ctx, UNUSED fr_cmd_info_t const *info)
Definition network.c:2273
fr_time_delta_t cpu_time
how much CPU time this worker has spent
Definition network.c:58
fr_control_t * control
the control plane
Definition network.c:123
bool blocked
is it blocked?
Definition network.c:82
static void fr_network_listen_callback(void const *data, size_t data_size, UNUSED fr_time_t now, void *uctx)
Handle a network control message callback for a new listener.
Definition network.c:1441
static void fr_network_post_close_event(fr_event_list_t *el, UNUSED fr_time_t now, void *uctx)
Definition network.c:1831
Associate a worker thread with a network thread.
Definition network.c:56
uint32_t max_outstanding
Definition network.h:46
#define PERROR(_fmt,...)
Definition log.h:233
#define DEBUG3(_fmt,...)
Definition log.h:271
#define PWARN(_fmt,...)
Definition log.h:232
#define DEBUG4(_fmt,...)
Definition log.h:272
#define HEXDUMP2(_data, _len, _fmt,...)
Definition log.h:739
#define RATE_LIMIT_GLOBAL(_log, _fmt,...)
Rate limit messages using a global limiting entry.
Definition log.h:658
void fr_event_service(fr_event_list_t *el)
Service any outstanding timer or file descriptor events.
Definition event.c:2222
int fr_event_pre_delete(fr_event_list_t *el, fr_event_status_cb_t callback, void *uctx)
Delete a pre-event callback from the event list.
Definition event.c:2020
int fr_event_fd_delete_handle(fr_event_fd_t *ef)
Remove a file descriptor from the event loop, by handle.
Definition event.c:1231
int fr_event_corral(fr_event_list_t *el, fr_time_t now, bool wait)
Gather outstanding timer and file descriptor events.
Definition event.c:2090
int fr_event_post_delete(fr_event_list_t *el, fr_event_post_cb_t callback, void *uctx)
Delete a post-event callback from the event list.
Definition event.c:2068
#define fr_time()
Definition event.c:60
int fr_event_pre_insert(fr_event_list_t *el, fr_event_status_cb_t callback, void *uctx)
Add a pre-event callback to the event list.
Definition event.c:1998
void fr_event_loop_exit(fr_event_list_t *el, int code)
Signal an event loop exit with the specified code.
Definition event.c:2400
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:1206
int fr_event_post_insert(fr_event_list_t *el, fr_event_post_cb_t callback, void *uctx)
Add a post-event callback to the event list.
Definition event.c:2045
A file descriptor/filter event.
Definition event.c:260
Stores all information relating to an event list.
Definition event.c:377
#define fr_log(_log, _lvl, _file, _line, _fmt,...)
Definition log.h:172
fr_log_lvl_t
Definition log.h:64
@ L_DBG
Only displayed when debugging is enabled.
Definition log.h:56
bool read_hexdump
Do we debug hexdump packets as they're read.
Definition listen.h:53
size_t num_messages
for the message ring buffer
Definition listen.h:57
bool non_socket_listener
special internal listener that does not use sockets.
Definition listen.h:47
char const * name
printable name for this socket - set by open
Definition listen.h:29
void const * app_instance
Definition listen.h:39
size_t default_message_size
copied from app_io, but may be changed
Definition listen.h:56
bool write_hexdump
Do we debug hexdump packets as they're written.
Definition listen.h:54
fr_app_t const * app
Definition listen.h:38
CONF_SECTION * server_cs
CONF_SECTION of the server.
Definition listen.h:42
bool no_write_callback
sometimes we don't need to do writes
Definition listen.h:46
int fd
file descriptor for this socket - set by open
Definition listen.h:28
fr_dict_t const * dict
dictionary for this listener
Definition listen.h:30
bool needs_full_setup
Set to true to avoid the short cut when adding the listener.
Definition listen.h:48
fr_app_io_t const * app_io
I/O path functions.
Definition listen.h:32
unsigned int uint32_t
long int ssize_t
unsigned char uint8_t
fr_message_set_t * fr_message_set_create(TALLOC_CTX *ctx, int num_messages, size_t message_size, size_t ring_buffer_size, bool unlimited_size)
Create a message set.
Definition message.c:127
void fr_message_and_data_reset(fr_message_set_t *ms, fr_message_t *m)
Cancel a reservation made by fr_message_and_data_reserve(), returning the slot to the set.
Definition message.c:1026
int fr_message_done(fr_message_t *m)
Mark a message as done.
Definition message.c:195
fr_message_t * fr_message_localize(TALLOC_CTX *ctx, fr_message_t *m, size_t message_size)
Localize a message by copying it to local storage.
Definition message.c:247
fr_message_t * fr_message_and_data_commit_with_leftover(fr_message_set_t *ms, fr_message_t *m, size_t actual_packet_size, size_t leftover, size_t reserve_size)
Allocate packet data for a message, and reserve a new message.
Definition message.c:1159
fr_message_t * fr_message_and_data_commit(fr_message_set_t *ms, fr_message_t *m, size_t total_size)
Commit a previously reserved message, allocating exactly total_size bytes of packet data.
Definition message.c:1051
fr_message_t * fr_message_and_data_alloc(fr_message_set_t *ms, size_t size)
Reserve and commit a message atomically.
Definition message.c:1108
fr_message_t * fr_message_and_data_reserve(fr_message_set_t *ms, size_t reserve_size)
Reserve a message.
Definition message.c:973
A Message set, composed of message headers and ring buffer data.
Definition message.c:94
size_t rb_size
cache-aligned size in the ring buffer
Definition message.h:51
fr_time_t when
when this message was sent
Definition message.h:47
uint8_t * data
pointer to the data in the ring buffer
Definition message.h:49
size_t data_size
size of the data in the ring buffer
Definition message.h:50
@ FR_MESSAGE_USED
Definition message.h:39
@ FR_MESSAGE_LOCALIZED
Definition message.h:40
fr_message_status_t status
free, used, done, etc.
Definition message.h:45
int fr_nonblock(UNUSED int fd)
Definition misc.c:293
fr_cmp_ret_t
Result of an ordering comparison.
Definition misc.h:50
static const conf_parser_t config[]
Definition base.c:162
#define fr_assert(_expr)
Definition rad_assert.h:37
#define DEBUG2(fmt,...)
static fr_app_io_t app_io
uint32_t fr_rand(void)
Return a 32-bit random number.
Definition rand.c:104
uint32_t fr_rb_num_elements(fr_rb_tree_t *tree)
Return how many nodes there are in a tree.
Definition rb.c:807
int fr_rb_find(void **found, fr_rb_tree_t const *tree, void const *data)
Find an element in the tree, returning the data, not the node.
Definition rb.c:586
void * fr_rb_iter_init_inorder(fr_rb_tree_t *tree, fr_rb_iter_inorder_t *iter)
Initialise an in-order iterator.
Definition rb.c:850
int fr_rb_delete(fr_rb_tree_t *tree, void const *data)
Remove node and free data (if a free function was specified)
Definition rb.c:767
int fr_rb_insert(fr_rb_tree_t *tree, void const *data)
Insert data into a tree.
Definition rb.c:637
void * fr_rb_iter_next_inorder(UNUSED fr_rb_tree_t *tree, fr_rb_iter_inorder_t *iter)
Return the next node.
Definition rb.c:876
#define fr_rb_inline_talloc_alloc(_ctx, _type, _field, _data_cmp, _data_free)
Allocs a red black that verifies elements are of a specific talloc type.
Definition rb.h:244
int fr_rb_flatten_inorder(TALLOC_CTX *ctx, void **out[], fr_rb_tree_t *tree)
Iterator structure for in-order traversal of an rbtree.
Definition rb.h:319
The main red black tree structure.
Definition rb.h:71
fr_ring_buffer_t * fr_ring_buffer_create(TALLOC_CTX *ctx, size_t size)
Create a ring buffer.
Definition ring_buffer.c:64
static char const * name
static char buff[sizeof("18446744073709551615")+3]
Definition size_tests.c:37
Definition log.h:93
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
Definition syserror.c:243
#define fr_table_str_by_value(_table, _number, _def)
Convert an integer to a string.
Definition table.h:804
#define talloc_get_type_abort_const
Definition talloc.h:117
#define talloc_strdup(_ctx, _str)
Definition talloc.h:149
static fr_time_delta_t fr_time_delta_from_msec(int64_t msec)
Definition time.h:575
static fr_time_delta_t fr_time_delta_add(fr_time_delta_t a, fr_time_delta_t b)
Definition time.h:255
#define fr_time_delta_lt(_a, _b)
Definition time.h:285
#define fr_time_wrap(_time)
Definition time.h:145
#define fr_time_delta_ispos(_a)
Definition time.h:290
static int8_t fr_time_cmp(fr_time_t a, fr_time_t b)
Compare two fr_time_t values.
Definition time.h:916
A time delta, a difference in time measured in nanoseconds.
Definition time.h:80
"server local" time.
Definition time.h:69
static fr_event_list_t * el
static fr_slen_t parent
Definition pair.h:864
void fr_perror(char const *fmt,...)
Print the current error to stderr with a prefix.
Definition strerror.c:737
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
Definition strerror.h:64
#define fr_strerror_const_push(_msg)
Definition strerror.h:227
#define fr_strerror_const(_msg)
Definition strerror.h:223
static fr_slen_t data
Definition value.h:1366
static fr_sbuff_err_t char size_t * len
Definition value.h:1061
fr_dict_t const * virtual_server_dict_by_cs(CONF_SECTION const *cs)
Return the namespace for specified CONF_SECTION.
fr_channel_t * fr_worker_channel_create(fr_worker_t *worker, TALLOC_CTX *ctx, fr_control_t *master, void *uctx)
Create a channel to the worker.
Definition worker.c:1728
int fr_worker_listen_cancel(fr_worker_t *worker, fr_listen_t const *li)
Definition worker.c:1754
fr_worker_config_t const * fr_worker_config(fr_worker_t *worker)
Definition worker.c:1820
A worker which takes packets from a master, and processes them.
Definition worker.c:91
int message_set_size
default start number of messages
Definition worker.h:73
#define FR_CONTROL_ID_INJECT
Definition worker.h:39
#define FR_CONTROL_ID_DIRECTORY
Definition worker.h:38
#define FR_CONTROL_ID_LISTEN
Definition worker.h:36
#define FR_CONTROL_ID_WORKER
Definition worker.h:37
int ring_buffer_size
default start size for the ring buffers
Definition worker.h:74