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