The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
proto_detail_work.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: 1db3139246e97dfa4062dd2e7714b691a1af2d64 $
19  * @file proto_detail_work.c
20  * @brief Detail handler for files
21  *
22  * @copyright 2017 The FreeRADIUS server project.
23  * @copyright 2017 Alan DeKok (aland@deployingradius.com)
24  */
25 #include <netdb.h>
26 #include <freeradius-devel/server/protocol.h>
27 #include <freeradius-devel/server/pair.h>
28 #include <freeradius-devel/server/main_loop.h>
29 #include <freeradius-devel/io/application.h>
30 #include <freeradius-devel/io/listen.h>
31 #include <freeradius-devel/util/syserror.h>
32 #include "proto_detail.h"
33 
34 #include <fcntl.h>
35 #include <sys/stat.h>
36 
37 #ifndef NDEBUG
38 #if 0
39 /*
40  * When we want detailed debugging here, without detailed server
41  * debugging.
42  */
43 #define MPRINT DEBUG
44 #else
45 #define MPRINT DEBUG3
46 #endif
47 #else
48 // No debugging, just remove the mprint entirely
49 #define MPRINT(_x, ...)
50 #endif
51 
52 typedef struct {
53  proto_detail_work_thread_t *parent; //!< talloc_parent is SLOW!
54  fr_time_t timestamp; //!< when we read the entry.
55  off_t done_offset; //!< where we're tracking the status
56 
57  int id; //!< for retransmission counters
58 
59  uint8_t *packet; //!< for retransmissions
60  size_t packet_len; //!< for retransmissions
61 
62  fr_retry_t retry; //!< our retry timers
63  fr_event_timer_t const *ev; //!< retransmission timer
64  fr_dlist_t entry; //!< for the retransmission list
66 
68  { FR_CONF_OFFSET("initial_rtx_time", proto_detail_work_t, retry_config.irt), .dflt = STRINGIFY(2) },
69  { FR_CONF_OFFSET("max_rtx_time", proto_detail_work_t, retry_config.mrt), .dflt = STRINGIFY(16) },
70 
71  /*
72  * Retransmit indefinitely, as v2 and v3 did.
73  */
74  { FR_CONF_OFFSET("max_rtx_count", proto_detail_work_t, retry_config.mrc), .dflt = STRINGIFY(0) },
75  /*
76  * ...again same as v2 and v3.
77  */
78  { FR_CONF_OFFSET("max_rtx_duration", proto_detail_work_t, retry_config.mrd), .dflt = STRINGIFY(0) },
79  { FR_CONF_OFFSET("max_outstanding", proto_detail_work_t, max_outstanding), .dflt = STRINGIFY(1) },
81 };
82 
83 
85  { FR_CONF_OFFSET_FLAGS("filename", CONF_FLAG_REQUIRED, proto_detail_work_t, filename_work ) },
86 
87  { FR_CONF_OFFSET("track", proto_detail_work_t, track_progress ) },
88 
89  { FR_CONF_OFFSET("retransmit", proto_detail_work_t, retransmit ), .dflt = "yes" },
90 
91  { FR_CONF_POINTER("limit", 0, CONF_FLAG_SUBSECTION, NULL), .subcs = (void const *) limit_config },
93 };
94 
95 static fr_dict_t const *dict_freeradius;
96 
99  { .out = &dict_freeradius, .proto = "freeradius" },
100 
101  { NULL }
102 };
103 
105 
108  { .out = &attr_packet_transmit_counter, .name = "Packet-Transmit-Counter", .type = FR_TYPE_UINT32, .dict = &dict_freeradius },
109  { NULL }
110 };
111 
112 /*
113  * All of the decoding is done by proto_detail.c
114  */
115 static int mod_decode(void const *instance, request_t *request, UNUSED uint8_t *const data, UNUSED size_t data_len)
116 {
117 
119  fr_detail_entry_t const *track = request->async->packet_ctx;
120  fr_pair_t *vp;
121 
122  request->client = inst->client;
123 
124  request->packet->id = track->id;
125  request->reply->id = track->id;
126  REQUEST_VERIFY(request);
127 
129  vp->vp_uint32 = track->retry.count;
130 
131  return 0;
132 }
133 
136  { 0 }
137 };
138 
141  { 0 }
142 };
143 
144 static ssize_t mod_read(fr_listen_t *li, void **packet_ctx, fr_time_t *recv_time_p, uint8_t *buffer, size_t buffer_len, size_t *leftover)
145 {
147  proto_detail_work_thread_t *thread = talloc_get_type_abort(li->thread_instance, proto_detail_work_thread_t);
148 
149  ssize_t data_size;
150  size_t packet_len;
151  fr_detail_entry_t *track;
152  uint8_t *partial, *end, *next, *p, *record_end;
153  uint8_t *stopped_search;
154  off_t done_offset;
155 
156  fr_assert(*leftover < buffer_len);
157  fr_assert(thread->fd >= 0);
158  fr_assert(thread->el);
159 
160  MPRINT("AT COUNT %d offset %ld", thread->count, (long) thread->read_offset);
161 
162  /*
163  * Process retransmissions before anything else in the
164  * file.
165  */
166  track = fr_dlist_head(&thread->list);
167  if (track) {
168  fr_dlist_remove(&thread->list, track);
169 
170  /*
171  * Don't over-write "leftover" bytes!
172  */
173  if (*leftover) {
174  fr_assert(thread->leftover == 0);
175  if (!thread->leftover_buffer) MEM(thread->leftover_buffer = talloc_array(thread, uint8_t, buffer_len));
176 
177  memcpy(thread->leftover_buffer, buffer, *leftover);
178  thread->leftover = *leftover;
179  *leftover = 0;
180  }
181 
182  fr_assert(buffer_len >= track->packet_len);
183  memcpy(buffer, track->packet, track->packet_len);
184 
185  DEBUG("Retrying packet %d (retransmission %u)", track->id, track->retry.count);
186  *packet_ctx = track;
187  *recv_time_p = track->timestamp;
188  return track->packet_len;
189  }
190 
191  /*
192  * If we decide that we're closing, ignore everything
193  * else in the file. Someone extended the file on us
194  * without locking it first. So too bad for them.
195  */
196  if (thread->closing) {
197  if (inst->track_progress) thread->read_offset = lseek(thread->fd, 0, SEEK_END);
198  return 0;
199  }
200 
201  /*
202  * Once a socket is ready, the network side tries to read
203  * many packets. So if we want to stop it from reading,
204  * we have to check this ourselves.
205  */
206  if (thread->outstanding >= inst->max_outstanding) {
207  fr_assert(thread->paused);
208  return 0;
209  }
210 
211  /*
212  * If we've cached leftover data from the ring buffer,
213  * copy it back.
214  */
215  if (thread->leftover) {
216  fr_assert(*leftover == 0);
217  fr_assert(thread->leftover < buffer_len);
218 
219  memcpy(buffer, thread->leftover_buffer, thread->leftover);
220  *leftover = thread->leftover;
221  thread->leftover = 0;
222  }
223 
224  /*
225  * Seek to the current read offset.
226  */
227  (void) lseek(thread->fd, thread->read_offset, SEEK_SET);
228 
229  /*
230  * There will be "leftover" bytes left over in the buffer
231  * from any previous read. At the start of the file,
232  * "leftover" will be zero.
233  */
234  partial = buffer + *leftover;
235 
236  MPRINT("READ leftover %zd", *leftover);
237 
238  /*
239  * Try to read as much data as possible.
240  */
241  if (!thread->eof) {
242  size_t room;
243 
244  room = buffer_len - *leftover;
245 
246  data_size = read(thread->fd, partial, room);
247  if (data_size < 0) {
248  ERROR("proto_detail (%s): Failed reading file %s: %s",
249  thread->name, thread->filename_work, fr_syserror(errno));
250  return -1;
251  }
252 
253  MPRINT("GOT %zd bytes", data_size);
254 
255  /*
256  * Remember the read offset, and whether we got EOF.
257  */
258  thread->read_offset = lseek(thread->fd, 0, SEEK_CUR);
259 
260  /*
261  * Only set EOF if there's no more data in the buffer to manage.
262  */
263  thread->eof = (data_size == 0) || (thread->read_offset == thread->file_size) || ((size_t) data_size < room);
264  if (thread->eof) {
265  MPRINT("Set EOF data_size %ld vs room %ld", data_size, room);
266  MPRINT("Set EOF read %ld vs file %ld", (long) thread->read_offset, (long) thread->file_size);
267  }
268  end = partial + data_size;
269 
270  } else {
271  MPRINT("READ UNTIL EOF");
272  /*
273  * We didn't read any more data from the file,
274  * but there should be data left in the buffer.
275  */
276  fr_assert(*leftover > 0);
277  end = buffer + *leftover;
278  }
279 
280 redo:
281  next = NULL;
282  stopped_search = end;
283 
284  /*
285  * Look for "end of record" marker, starting from the
286  * beginning of the buffer.
287  *
288  * Note that all of the data MUST be printable, and raw
289  * LFs are forbidden in attribute contents.
290  */
291  fr_assert((buffer + thread->last_search) <= end);
292 
293  MPRINT("Starting search from offset %ld", thread->last_search);
294 
295  p = buffer + thread->last_search;
296  while (p < end) {
297  if (p[0] != '\n') {
298  p++;
299  continue;
300  }
301  if ((p + 1) == end) {
302  /*
303  * Remember the last LF, so if the next
304  * read starts with a LF, we can find the
305  * end of record marker.
306  */
307  stopped_search = p;
308  break; /* no more data */
309  }
310 
311  if (p[1] == '\n') {
312  p[0] = '\0';
313  p[1] = '\0';
314  next = p + 2;
315  stopped_search = next;
316  break;
317  }
318 
319  /*
320  * If we're not at EOF, and we're not at end of
321  * record, every line MUST have a leading tab.
322  */
323  if (p[1] != '\t') {
324  ERROR("proto_detail (%s): Malformed line found at offset %zu in file %s",
325  thread->name, (size_t)((p - buffer) + thread->header_offset),
326  thread->filename_work);
327  return -1;
328  }
329 
330  /*
331  * Smash the \n with zero, so that each line can
332  * be parsed individually.
333  */
334  p[0] = '\0';
335 
336  /*
337  * Skip the \n\t
338  */
339  if ((p + 2) >= end) {
340  stopped_search = p;
341  break;
342  }
343 
344  p += 2;
345 
346  /*
347  * Skip attribute name
348  */
349  while ((p < end) && !isspace((uint8_t) *p)) p++;
350 
351  /*
352  * Not enough room for " = ", skip this sanity
353  * check, and just search for a \n on the next
354  * round through the loop.
355  */
356  if ((end - p) < 3) {
357  stopped_search = p;
358  break;
359  }
360 
361  /*
362  * Check for " = ". If the line doesn't contain
363  * this, it's malformed.
364  */
365  if (memcmp(p, " = ", 3) != 0) {
366  ERROR("proto_detail (%s): Malformed line found at offset %zu: %.*s of file %s",
367  thread->name,
368  (size_t)((p - buffer) + thread->header_offset), (int) (end - p), p,
369  thread->filename_work);
370  return -1;
371  }
372 
373  /*
374  * Skip the " = ", and go back to the top of the
375  * loop where we check for the next \n.
376  */
377  p += 3;
378  }
379 
380  thread->last_search = (stopped_search - buffer);
381 
382  /*
383  * If there is a next record, remember how large this
384  * record is, and update "leftover" bytes.
385  */
386  if (next) {
387  packet_len = next - buffer;
388  *leftover = end - next;
389 
390  MPRINT("FOUND next at %zd, leftover is %zd", packet_len, *leftover);
391 
392  } else if (!thread->eof) {
393  if ((size_t) (end - buffer) == buffer_len) {
394  ERROR("proto_detail (%s): Too large entry (>%d bytes) found at offset %zu: %.*s of file %s",
395  thread->name, (int) buffer_len,
396  (size_t)((p - buffer) + thread->header_offset), (int) (end - p), p,
397  thread->filename_work);
398  return -1;
399  }
400 
401  /*
402  * We're not at EOF, and there is no "next"
403  * entry. Remember all of the leftover data in
404  * the buffer, and ask the caller to call us when
405  * there's more data.
406  */
407  *leftover = end - buffer;
408  MPRINT("Not at EOF, and no next. Leftover is %zd", *leftover);
409  return 0;
410 
411  } else {
412  /*
413  * Else we're at EOF, it's OK to not have an "end
414  * of record" marker. We just eat all of the
415  * remaining data.
416  */
417  packet_len = end - buffer;
418  *leftover = 0;
419 
420  MPRINT("NO end of record, but at EOF, found %zd leftover is 0", packet_len);
421  }
422 
423  /*
424  * Too big? Ignore it.
425  *
426  * @todo - skip the record, using memmove() etc.
427  */
428  if (packet_len > inst->parent->max_packet_size) {
429  DEBUG("Ignoring 'too large' entry at offset %zu of %s",
430  (size_t) thread->header_offset, thread->filename_work);
431  DEBUG("Entry size %lu is greater than allowed maximum %u",
432  packet_len, inst->parent->max_packet_size);
433  skip_record:
434  MPRINT("Skipping record");
435  if (next) {
436  memmove(buffer, next, (end - next));
437  data_size = (end - next);
438  *leftover = 0;
439  end = buffer + data_size;
440  thread->last_search = 0;
441 
442  /*
443  * No more data, we're done.
444  */
445  if (end == buffer) return 0;
446  goto redo;
447  }
448 
449  fr_assert(*leftover == 0);
450  goto done;
451  }
452 
453  /*
454  * Search for the "Timestamp" attribute. We overload
455  * that to track which entries have been used.
456  */
457  record_end = buffer + packet_len;
458  p = buffer;
459  done_offset = 0;
460 
461  while (p < record_end) {
462  if (*p != '\0') {
463  p++;
464  continue;
465  }
466 
467  p++;
468  if (p == record_end) break;
469 
470  if (((record_end - p) >= 5) &&
471  (memcmp(p, "\tDone", 5) == 0)) {
472  goto skip_record;
473  }
474 
475  if (((record_end - p) > 10) &&
476  (memcmp(p, "\tTimestamp", 10) == 0)) {
477  p++;
478  done_offset = thread->header_offset + (p - buffer);
479  }
480  }
481 
482  /*
483  * Allocate the tracking entry.
484  */
485  track = talloc_zero(thread, fr_detail_entry_t);
486  track->parent = thread;
487  track->timestamp = fr_time();
488  track->id = thread->count++;
489 
490  track->done_offset = done_offset;
491  if (inst->retransmit) {
492  track->packet = talloc_memdup(track, buffer, packet_len);
493  track->packet_len = packet_len;
494  }
495 
496  /*
497  * We've read one more packet.
498  */
499  thread->header_offset += packet_len;
500 
501  *packet_ctx = track;
502  *recv_time_p = track->timestamp;
503 
504 done:
505  /*
506  * If we're at EOF, mark us as "closing".
507  */
508  if (thread->eof) {
509  fr_assert(!thread->closing);
510  thread->closing = (*leftover == 0);
511  MPRINT("AT EOF, BUT CLOSING %d", thread->closing);
512  }
513 
514  thread->outstanding++;
515 
516  /*
517  * Pause reading until such time as we need more packets.
518  */
519  if (!thread->paused && (thread->outstanding >= inst->max_outstanding)) {
520  (void) fr_event_filter_update(thread->el, thread->fd, FR_EVENT_FILTER_IO, pause_read);
521  thread->paused = true;
522 
523  /*
524  * Back up so that read() knows there's more data.
525  */
526  if (*leftover) (void) lseek(thread->fd, thread->read_offset - 1, SEEK_SET);
527  }
528 
529  /*
530  * Next time, start searching from the start of the
531  * buffer.
532  */
533  thread->last_search = 0;
534 
535  MPRINT("Returning NUM %u - %.*s", thread->outstanding, (int) packet_len, buffer);
536  return packet_len;
537 }
538 
539 
540 static void work_retransmit(UNUSED fr_event_list_t *el, UNUSED fr_time_t now, void *uctx)
541 {
542  fr_detail_entry_t *track = talloc_get_type_abort(uctx, fr_detail_entry_t);
543  proto_detail_work_thread_t *thread = track->parent;
544 
545  DEBUG("%s - retransmitting packet %d", thread->name, track->id);
546 
547  fr_dlist_insert_tail(&thread->list, track);
548 
549  if (thread->paused && (thread->outstanding < thread->inst->max_outstanding)) {
550  (void) fr_event_filter_update(thread->el, thread->fd, FR_EVENT_FILTER_IO, resume_read);
551  thread->paused = false;
552  }
553 
554  fr_assert(thread->fd >= 0);
555 
556  /*
557  * Seek to the START of the file, so that the FD will
558  * always return ready.
559  *
560  * The mod_read() function will take care of seeking to
561  * the correct read offset.
562  */
563  (void) lseek(thread->fd, 0, SEEK_SET);
564 
565 #ifdef __linux__
566  fr_network_listen_read(thread->nr, thread->listen);
567 #endif
568 }
569 
570 static ssize_t mod_write(fr_listen_t *li, void *packet_ctx, UNUSED fr_time_t request_time,
571  uint8_t *buffer, size_t buffer_len, UNUSED size_t written)
572 {
574  proto_detail_work_thread_t *thread = talloc_get_type_abort(li->thread_instance, proto_detail_work_thread_t);
575  fr_detail_entry_t *track = packet_ctx;
576 
577  if (buffer_len < 1) return -1;
578 
579  fr_assert(thread->outstanding > 0);
580  fr_assert(thread->fd >= 0);
581 
582  if (!buffer[0]) {
583  if (!inst->retransmit) goto mark_done;
584 
585  if (fr_time_eq(track->retry.start, fr_time_wrap(0))) {
586  fr_retry_init(&track->retry, fr_time(), &inst->retry_config);
587  } else {
588  fr_retry_state_t state;
589 
590  state = fr_retry_next(&track->retry, fr_time());
591  if (state == FR_RETRY_MRC) {
592  DEBUG("%s - packet %d failed after %u retransmissions",
593  thread->name, track->id, track->retry.count);
594  goto fail;
595 
596  }
597 
598  if (state == FR_RETRY_MRD) {
599  DEBUG("%s - packet %d failed after %u seconds",
600  thread->name, track->id,
601  (unsigned int) fr_time_delta_to_sec(inst->retry_config.mrd));
602  goto fail;
603  }
604  }
605 
606  DEBUG("%s - packet %d failed during processing. Will retransmit in %.6fs",
607  thread->name, track->id, fr_time_delta_unwrap(track->retry.rt) / (double)NSEC);
608 
609  if (fr_event_timer_at(thread, thread->el, &track->ev,
610  track->retry.next, work_retransmit, track) < 0) {
611  ERROR("%s - Failed inserting retransmission timeout", thread->name);
612  fail:
613  if (inst->track_progress && (track->done_offset > 0)) goto mark_done;
614  goto free_track;
615  }
616 
617  if (!thread->paused && (thread->outstanding >= inst->max_outstanding)) {
618  (void) fr_event_filter_update(thread->el, thread->fd, FR_EVENT_FILTER_IO, pause_read);
619  thread->paused = true;
620  }
621 
622  return 1;
623 
624  } else if (inst->track_progress && (track->done_offset > 0)) {
625  mark_done:
626  /*
627  * Seek to the entry, mark it as done, and then seek to
628  * the point in the file where we were reading from.
629  */
630  (void) lseek(thread->fd, track->done_offset, SEEK_SET);
631  if (write(thread->fd, "Done", 4) < 0) {
632  ERROR("%s - Failed marking entry as done: %s", thread->name, fr_syserror(errno));
633  }
634  (void) lseek(thread->fd, thread->read_offset, SEEK_SET);
635  }
636 
637 free_track:
638  thread->outstanding--;
639 
640  /*
641  * If we need to read some more packet, let's do so.
642  */
643  if (thread->paused && (thread->outstanding < inst->max_outstanding)) {
644  (void) fr_event_filter_update(thread->el, thread->fd, FR_EVENT_FILTER_IO, resume_read);
645  thread->paused = false;
646 
647  /*
648  * And seek to the start of the file, so that the
649  * reader gets activated again. The reader will
650  * lseek() to the read offset, so this seek is fine.
651  */
652  (void) lseek(thread->fd, 0, SEEK_SET);
653  }
654 
655  /*
656  * @todo - add a used / free pool for these
657  */
658  talloc_free(track);
659 
660  /*
661  * Close the socket if we're at EOF, and there are no
662  * outstanding replies to deal with.
663  */
664  if (thread->closing && !thread->outstanding) {
665  MPRINT("WRITE ASKED TO CLOSE");
666  return 0;
667  }
668 
669  MPRINT("WRITE RETURN B %ld", buffer_len);
670  return buffer_len;
671 }
672 
673 /** Open a detail listener
674  *
675  */
676 static int mod_open(fr_listen_t *li)
677 {
679  proto_detail_work_thread_t *thread = talloc_get_type_abort(li->thread_instance, proto_detail_work_thread_t);
680 
681  fr_dlist_init(&thread->list, fr_detail_entry_t, entry);
682 
683  /*
684  * Open the file if we haven't already been given one.
685  */
686  if (thread->fd < 0) {
687  thread->filename_work = talloc_strdup(inst, inst->filename_work);
688 
689  li->fd = thread->fd = open(thread->filename_work, inst->mode);
690  if (thread->fd < 0) {
691  cf_log_err(inst->cs, "Failed opening %s: %s", thread->filename_work, fr_syserror(errno));
692  return -1;
693  }
694  }
695 
696  /*
697  * If we're tracking progress, learn where the EOF is.
698  */
699  if (inst->track_progress) {
700  struct stat buf;
701 
702  if (fstat(thread->fd, &buf) < 0) {
703  cf_log_err(inst->cs, "Failed examining %s: %s", thread->filename_work, fr_syserror(errno));
704  return -1;
705  }
706 
707  thread->file_size = buf.st_size;
708  } else {
709  /*
710  * Avoid triggering erroneous EOF.
711  */
712  thread->file_size = 1;
713  }
714 
715  fr_assert(thread->name == NULL);
716  fr_assert(thread->filename_work != NULL);
717  thread->name = talloc_typed_asprintf(thread, "detail_work reading file %s", thread->filename_work);
718 
719  /*
720  * Linux doesn't like us adding write callbacks for FDs
721  * which reference files. Since the callback is only
722  * used when the FD blocks, and files don't (mostly)
723  * block, we just mark this read-only.
724  *
725  * The code in src/lib/io/network.c will call the
726  * mod_write() callback on any write, even if the
727  * listener is marked "read_only"
728  */
729  li->no_write_callback = true;
730 
731  return 0;
732 }
733 
734 
736 {
738 
739  /*
740  * One less worker... we check for "0" because of the
741  * hacks in proto_detail which let us start up with
742  * "transport = work" for debugging purposes.
743  */
744  if (thread->file_parent) {
745  pthread_mutex_lock(&thread->file_parent->worker_mutex);
746  if (thread->file_parent->num_workers > 0) thread->file_parent->num_workers--;
747  pthread_mutex_unlock(&thread->file_parent->worker_mutex);
748  }
749 
750  DEBUG("Closing %sdetail worker file %s", thread->outstanding == 0 ? "and deleting " : "", thread->name);
751 
752 #ifdef NOTE_REVOKE
753  fr_event_fd_delete(thread->el, thread->fd, FR_EVENT_FILTER_VNODE);
754 #endif
755  fr_event_fd_delete(thread->el, thread->fd, FR_EVENT_FILTER_IO);
756 
757  if (thread->outstanding == 0) unlink(thread->filename_work);
758 
759  close(thread->fd);
760  thread->fd = -1;
761 
762  /*
763  * If we've been spawned from proto_detail_file, clean
764  * ourselves up, including our listener.
765  */
766  if (thread->listen) {
767  talloc_free(thread->listen);
768  }
769 
770  if (inst->parent->exit_when_done) {
771  INFO("Done reading detail files, process will now exit");
772 
773  /*
774  * The least hacky way of signalling that the server
775  * should exit. This is an almost identical code
776  * path as receiving a SIGTERM.
777  */
779  }
780 
781  return 0;
782 }
783 
784 
785 /** Close a detail listener
786  *
787  */
788 static int mod_close(fr_listen_t *li)
789 {
790  proto_detail_work_thread_t *thread = talloc_get_type_abort(li->thread_instance, proto_detail_work_thread_t);
791 
792  return mod_close_internal(thread);
793 }
794 
795 #ifdef NOTE_REVOKE
796 static void mod_revoke(UNUSED fr_event_list_t *el, UNUSED int fd, UNUSED int flags, void *uctx)
797 {
798  proto_detail_work_thread_t *thread = talloc_get_type_abort(uctx, proto_detail_work_thread_t);
799 
800  /*
801  * The underlying file system is gone. Stop reading the
802  * file, destroy all of the IO handlers, and delete everything.
803  */
804  DEBUG("Detail worker %s had file system unmounted. Stopping.", thread->name);
805  mod_close_internal(thread);
806 }
807 #endif
808 
809 
810 /** Set the event list for a new IO instance
811  *
812  * @param[in] li the listener
813  * @param[in] el the event list
814  * @param[in] nr context from the network side
815  */
816 static void mod_event_list_set(fr_listen_t *li, fr_event_list_t *el, void *nr)
817 {
818  proto_detail_work_thread_t *thread = talloc_get_type_abort(li->thread_instance, proto_detail_work_thread_t);
819 
820 #ifdef NOTE_REVOKE
821  fr_event_vnode_func_t funcs;
822 
823  memset(&funcs, 0, sizeof(funcs));
824  funcs.revoke = mod_revoke;
825 
826  if (fr_event_filter_insert(thread, NULL, el, thread->fd, FR_EVENT_FILTER_VNODE, &funcs, NULL, thread) < 0) {
827  WARN("Failed to add event watching for unmounted file system");
828  }
829 #endif
830 
831  thread->el = el;
832  thread->nr = nr;
833 }
834 
835 
836 static char const *mod_name(fr_listen_t *li)
837 
838 { proto_detail_work_thread_t *thread = talloc_get_type_abort(li->thread_instance, proto_detail_work_thread_t);
839 
840  return thread->name;
841 }
842 
843 static int mod_instantiate(module_inst_ctx_t const *mctx)
844 {
845  proto_detail_work_t *inst = talloc_get_type_abort(mctx->inst->data, proto_detail_work_t);
846  fr_client_t *client;
847 
848  client = inst->client = talloc_zero(inst, fr_client_t);
849  if (!inst->client) return 0;
850 
851  client->ipaddr.af = AF_INET;
852  client->ipaddr.addr.v4.s_addr = htonl(INADDR_NONE);
853  client->src_ipaddr = client->ipaddr;
854 
855  client->longname = client->shortname = client->secret = inst->filename;
856  client->nas_type = talloc_strdup(client, "other");
857 
858  return 0;
859 }
860 
861 static int mod_bootstrap(module_inst_ctx_t const *mctx)
862 {
863  proto_detail_work_t *inst = talloc_get_type_abort(mctx->inst->data, proto_detail_work_t);
864  CONF_SECTION *cs = mctx->inst->conf;
865  dl_module_inst_t const *dl_inst;
866 
867  /*
868  * Find the dl_module_inst_t holding our instance data
869  * so we can find out what the parent of our instance
870  * was.
871  */
872  dl_inst = dl_module_instance_by_data(mctx->inst->data);
873  fr_assert(dl_inst);
874 
875  inst->parent = talloc_get_type_abort(dl_inst->parent->data, proto_detail_t);
876  inst->cs = cs;
877 
878  if (inst->track_progress) {
879  inst->mode = O_RDWR;
880  } else {
881  inst->mode = O_RDONLY;
882  }
883 
884  if (inst->retransmit) {
885  FR_TIME_DELTA_BOUND_CHECK("limit.initial_rtx_time", inst->retry_config.irt, >=, fr_time_delta_from_sec(1));
886  FR_TIME_DELTA_BOUND_CHECK("limit.initial_rtx_time", inst->retry_config.irt, <=, fr_time_delta_from_sec(60));
887 
888  /*
889  * If you need more than this, just set it to
890  * "0", and check Packet-Transmit-Count manually.
891  */
892  FR_INTEGER_BOUND_CHECK("limit.max_rtx_count", inst->retry_config.mrc, <=, 20);
893  FR_TIME_DELTA_BOUND_CHECK("limit.max_rtx_duration", inst->retry_config.mrd, <=, fr_time_delta_from_sec(600));
894 
895  /*
896  * This is a reasonable value.
897  */
898  FR_TIME_DELTA_BOUND_CHECK("limit.max_rtx_timer", inst->retry_config.mrt, <=, fr_time_delta_from_sec(30));
899  }
900 
901  FR_INTEGER_BOUND_CHECK("limit.max_outstanding", inst->max_outstanding, >=, 1);
902 
903  return 0;
904 }
905 
906 
907 /** Private interface for use by proto_detail_file
908  *
909  */
912  .common = {
913  .magic = MODULE_MAGIC_INIT,
914  .name = "detail_work",
915  .config = file_listen_config,
916  .inst_size = sizeof(proto_detail_work_t),
917  .thread_inst_size = sizeof(proto_detail_work_thread_t),
918  .bootstrap = mod_bootstrap,
919  .instantiate = mod_instantiate
920  },
921  .default_message_size = 65536,
922  .default_reply_size = 32,
923 
924  .open = mod_open,
925  .close = mod_close,
926  .read = mod_read,
927  .decode = mod_decode,
928  .write = mod_write,
929  .event_list_set = mod_event_list_set,
930  .get_name = mod_name,
931 };
static int const char char buffer[256]
Definition: acutest.h:574
module_t common
Common fields to all loadable modules.
Definition: app_io.h:34
Public structure describing an I/O path for a protocol.
Definition: app_io.h:33
#define STRINGIFY(x)
Definition: build.h:195
#define UNUSED
Definition: build.h:313
#define CONF_PARSER_TERMINATOR
Definition: cf_parse.h:626
#define FR_INTEGER_BOUND_CHECK(_name, _var, _op, _bound)
Definition: cf_parse.h:486
#define FR_CONF_OFFSET(_name, _struct, _field)
conf_parser_t which parses a single CONF_PAIR, writing the result to a field in a struct
Definition: cf_parse.h:268
#define FR_CONF_POINTER(_name, _type, _flags, _res_p)
conf_parser_t which parses a single CONF_PAIR producing a single global result
Definition: cf_parse.h:310
#define FR_CONF_OFFSET_FLAGS(_name, _flags, _struct, _field)
conf_parser_t which parses a single CONF_PAIR, writing the result to a field in a struct
Definition: cf_parse.h:256
#define FR_TIME_DELTA_BOUND_CHECK(_name, _var, _op, _bound)
Definition: cf_parse.h:497
@ CONF_FLAG_REQUIRED
Error out if no matching CONF_PAIR is found, and no dflt value is set.
Definition: cf_parse.h:406
@ CONF_FLAG_SUBSECTION
Instead of putting the information into a configuration structure, the configuration file routines MA...
Definition: cf_parse.h:400
Defines a CONF_PAIR to C data type mapping.
Definition: cf_parse.h:563
A section grouping multiple CONF_PAIR.
Definition: cf_priv.h:89
#define cf_log_err(_cf, _fmt,...)
Definition: cf_util.h:265
static int max_outstanding
Definition: channel_test.c:51
#define ERROR(fmt,...)
Definition: dhcpclient.c:41
#define DEBUG(fmt,...)
Definition: dhcpclient.c:39
fr_dict_attr_t const ** out
Where to write a pointer to the resolved fr_dict_attr_t.
Definition: dict.h:250
fr_dict_t const ** out
Where to write a pointer to the loaded/resolved fr_dict_t.
Definition: dict.h:263
Specifies an attribute which must be present for the module to function.
Definition: dict.h:249
Specifies a dictionary which must be loaded/loadable for the module to function.
Definition: dict.h:262
dl_module_inst_t const * dl_module_instance_by_data(void const *data)
Lookup a dl_module_inst_t via instance data.
Definition: dl_module.c:215
void *_CONST data
Module instance's parsed configuration.
Definition: dl_module.h:165
#define MODULE_MAGIC_INIT
Stop people using different module/library/server versions together.
Definition: dl_module.h:65
CONF_SECTION *_CONST conf
Module's instance configuration.
Definition: dl_module.h:166
dl_module_inst_t const *_CONST parent
Parent module's instance (if any).
Definition: dl_module.h:167
A module/inst tuple.
Definition: dl_module.h:162
#define fr_dlist_init(_head, _type, _field)
Initialise the head structure of a doubly linked list.
Definition: dlist.h:260
static void * fr_dlist_head(fr_dlist_head_t const *list_head)
Return the HEAD item of a list or NULL if the list is empty.
Definition: dlist.h:486
static int fr_dlist_insert_tail(fr_dlist_head_t *list_head, void *ptr)
Insert an item into the tail of a list.
Definition: dlist.h:378
static void * fr_dlist_remove(fr_dlist_head_t *list_head, void *ptr)
Remove an item from the list.
Definition: dlist.h:638
Entry in a doubly linked list.
Definition: dlist.h:41
@ FR_EVENT_FILTER_VNODE
Filter for vnode subfilters.
Definition: event.h:63
@ FR_EVENT_FILTER_IO
Combined filter for read/write functions/.
Definition: event.h:62
#define fr_event_filter_update(...)
Definition: event.h:224
#define fr_event_filter_insert(...)
Definition: event.h:219
#define FR_EVENT_RESUME(_s, _f)
Re-add the filter for a func from kevent.
Definition: event.h:110
#define FR_EVENT_SUSPEND(_s, _f)
Temporarily remove the filter for a func from kevent.
Definition: event.h:94
#define fr_event_timer_at(...)
Definition: event.h:250
Callbacks for the FR_EVENT_FILTER_IO filter.
Definition: event.h:173
Structure describing a modification to a filter's state.
Definition: event.h:75
Callbacks for the FR_EVENT_FILTER_VNODE filter.
Definition: event.h:180
int af
Address family.
Definition: inet.h:64
union fr_ipaddr_t::@121 addr
void const * app_io_instance
I/O path configuration context.
Definition: listen.h:32
void * thread_instance
thread / socket context
Definition: listen.h:33
bool no_write_callback
sometimes we don't need to do writes
Definition: listen.h:44
int fd
file descriptor for this socket - set by open
Definition: listen.h:28
void fr_network_listen_read(fr_network_t *nr, fr_listen_t *li)
Signal the network to read from a listener.
Definition: network.c:311
fr_ipaddr_t ipaddr
IPv4/IPv6 address of the host.
Definition: client.h:80
char const * secret
Secret PSK.
Definition: client.h:87
fr_ipaddr_t src_ipaddr
IPv4/IPv6 address to send responses from (family must match ipaddr).
Definition: client.h:81
char const * nas_type
Type of client (arbitrary).
Definition: client.h:99
char const * longname
Client identifier.
Definition: client.h:84
char const * shortname
Client nickname.
Definition: client.h:85
Describes a host allowed to send packets to the server.
Definition: client.h:77
talloc_free(reap)
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:1253
Stores all information relating to an event list.
Definition: event.c:411
A timer event.
Definition: event.c:102
void main_loop_signal_raise(int flag)
Definition: main_loop.c:79
@ RADIUS_SIGNAL_SELF_TERM
Definition: main_loop.h:37
@ FR_TYPE_UINT32
32 Bit unsigned integer.
Definition: merged_model.c:99
long int ssize_t
Definition: merged_model.c:24
unsigned char uint8_t
Definition: merged_model.c:30
long long int off_t
Definition: merged_model.c:22
unsigned long int size_t
Definition: merged_model.c:25
dl_module_inst_t const * inst
Dynamic loader API handle for the module.
Definition: module_ctx.h:52
Temporary structure to hold arguments for instantiation calls.
Definition: module_ctx.h:51
Detail master protocol handler.
struct proto_detail_work_s proto_detail_work_t
Definition: proto_detail.h:76
size_t last_search
where we last searched in the buffer MUST be offset, as the buffers can change.
Definition: proto_detail.h:134
proto_detail_work_thread_t * file_parent
thread instance of the directory reader that spawned us
Definition: proto_detail.h:117
bool paused
Is reading paused?
Definition: proto_detail.h:127
int count
number of packets we read from this file.
Definition: proto_detail.h:129
bool eof
are we at EOF on reading?
Definition: proto_detail.h:125
fr_listen_t * listen
talloc_parent() is slow
Definition: proto_detail.h:116
char const * name
debug name for printing
Definition: proto_detail.h:108
int num_workers
number of workers
Definition: proto_detail.h:144
uint16_t max_outstanding
number of packets to run in parallel
Definition: proto_detail.h:92
fr_event_list_t * el
for various timers
Definition: proto_detail.h:114
off_t file_size
size of the file
Definition: proto_detail.h:137
pthread_mutex_t worker_mutex
for the workers
Definition: proto_detail.h:143
char const * filename_work
work file name
Definition: proto_detail.h:119
off_t header_offset
offset of the current header we're reading
Definition: proto_detail.h:138
proto_detail_work_t const * inst
instance data
Definition: proto_detail.h:109
fr_dlist_head_t list
for retransmissions
Definition: proto_detail.h:120
int fd
file descriptor
Definition: proto_detail.h:111
bool closing
we should be closing the file
Definition: proto_detail.h:126
off_t read_offset
where we're reading from in filename_work
Definition: proto_detail.h:139
uint32_t outstanding
number of currently outstanding records;
Definition: proto_detail.h:122
fr_network_t * nr
for Linux-specific callbacks
Definition: proto_detail.h:115
fr_dict_attr_autoload_t proto_detail_work_dict_attr[]
static const conf_parser_t file_listen_config[]
static void work_retransmit(UNUSED fr_event_list_t *el, UNUSED fr_time_t now, void *uctx)
static ssize_t mod_read(fr_listen_t *li, void **packet_ctx, fr_time_t *recv_time_p, uint8_t *buffer, size_t buffer_len, size_t *leftover)
#define MPRINT
fr_event_timer_t const * ev
retransmission timer
static int mod_decode(void const *instance, request_t *request, UNUSED uint8_t *const data, UNUSED size_t data_len)
static fr_dict_t const * dict_freeradius
static int mod_bootstrap(module_inst_ctx_t const *mctx)
proto_detail_work_thread_t * parent
talloc_parent is SLOW!
fr_dlist_t entry
for the retransmission list
static void mod_event_list_set(fr_listen_t *li, fr_event_list_t *el, void *nr)
Set the event list for a new IO instance.
static int mod_open(fr_listen_t *li)
Open a detail listener.
size_t packet_len
for retransmissions
fr_retry_t retry
our retry timers
uint8_t * packet
for retransmissions
fr_time_t timestamp
when we read the entry.
static char const * mod_name(fr_listen_t *li)
fr_dict_autoload_t proto_detail_work_dict[]
static fr_event_update_t pause_read[]
static conf_parser_t limit_config[]
fr_app_io_t proto_detail_work
Private interface for use by proto_detail_file.
static int mod_close_internal(proto_detail_work_thread_t *thread)
int id
for retransmission counters
static int mod_close(fr_listen_t *li)
Close a detail listener.
off_t done_offset
where we're tracking the status
static fr_dict_attr_t const * attr_packet_transmit_counter
static ssize_t mod_write(fr_listen_t *li, void *packet_ctx, UNUSED fr_time_t request_time, uint8_t *buffer, size_t buffer_len, UNUSED size_t written)
static int mod_instantiate(module_inst_ctx_t const *mctx)
static fr_event_update_t resume_read[]
#define pair_update_request(_attr, _da)
Definition: radclient-ng.c:60
static bool done
Definition: radclient.c:80
#define WARN(fmt,...)
Definition: radclient.h:47
#define INFO(fmt,...)
Definition: radict.c:54
#define REQUEST_VERIFY(_x)
Definition: request.h:275
static conf_parser_t retry_config[]
Definition: rlm_tacacs.c:38
fr_assert(0)
MEM(pair_append_request(&vp, attr_eap_aka_sim_identity) >=0)
eap_aka_sim_process_conf_t * inst
fr_pair_t * vp
#define fr_time()
Allow us to arbitrarily manipulate time.
Definition: state_test.c:8
Stores an attribute, a value and various bits of other data.
Definition: pair.h:68
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
Definition: syserror.c:243
char * talloc_typed_asprintf(TALLOC_CTX *ctx, char const *fmt,...)
Call talloc vasprintf, setting the type on the new chunk correctly.
Definition: talloc.c:380
#define talloc_get_type_abort_const
Definition: talloc.h:270
static int64_t fr_time_delta_unwrap(fr_time_delta_t time)
Definition: time.h:154
static fr_time_delta_t fr_time_delta_from_sec(int64_t sec)
Definition: time.h:588
#define fr_time_wrap(_time)
Definition: time.h:145
#define fr_time_eq(_a, _b)
Definition: time.h:241
static int64_t fr_time_delta_to_sec(fr_time_delta_t delta)
Definition: time.h:645
#define NSEC
Definition: time.h:377
"server local" time.
Definition: time.h:69
close(uq->fd)
static fr_event_list_t * el
fr_retry_state_t fr_retry_next(fr_retry_t *r, fr_time_t now)
Initialize a retransmission counter.
Definition: retry.c:84
void fr_retry_init(fr_retry_t *r, fr_time_t now, fr_retry_config_t const *config)
Initialize a retransmission counter.
Definition: retry.c:36
fr_time_t start
when we started the retransmission
Definition: retry.h:43
fr_time_delta_t rt
retransmit interval
Definition: retry.h:46
fr_retry_state_t
Definition: retry.h:55
@ FR_RETRY_MRC
reached maximum retransmission count
Definition: retry.h:57
@ FR_RETRY_MRD
reached maximum retransmission duration
Definition: retry.h:58
uint32_t count
number of sent packets
Definition: retry.h:47
fr_time_t next
when the next timer should be set
Definition: retry.h:44
static fr_slen_t data
Definition: value.h:1259