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