The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
rlm_radius_udp.c
Go to the documentation of this file.
1 /*
2  * This program is is free software; you can redistribute it and/or modify
3  * it under the terms of the GNU General Public License as published by
4  * the Free Software Foundation; either version 2 of the License, or (at
5  * your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software
14  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15  */
16 
17 /**
18  * $Id: aabc07be690e2376cc46eceb382d3ab6d60c1b99 $
19  * @file rlm_radius_udp.c
20  * @brief RADIUS UDP transport
21  *
22  * @copyright 2017 Network RADIUS SAS
23  * @copyright 2020 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
24  */
25 RCSID("$Id: aabc07be690e2376cc46eceb382d3ab6d60c1b99 $")
26 
27 #include <freeradius-devel/io/application.h>
28 #include <freeradius-devel/io/listen.h>
29 #include <freeradius-devel/io/pair.h>
30 #include <freeradius-devel/missing.h>
31 #include <freeradius-devel/server/connection.h>
32 #include <freeradius-devel/util/debug.h>
33 #include <freeradius-devel/util/heap.h>
34 #include <freeradius-devel/util/udp.h>
35 
36 #include <sys/socket.h>
37 
38 #include "rlm_radius.h"
39 #include "track.h"
40 
41 /*
42  * Macro to simplify checking packets before calling decode(), so that
43  * it gets a known valid length and no longer calls fr_radius_ok() itself.
44  */
45 #define check(_handle, _len_p) fr_radius_ok((_handle)->buffer, (size_t *)(_len_p), \
46  (_handle)->thread->inst->parent->max_attributes, false, NULL)
47 
48 /** Static configuration for the module.
49  *
50  */
51 typedef struct {
52  rlm_radius_t *parent; //!< rlm_radius instance.
54 
55  fr_ipaddr_t dst_ipaddr; //!< IP of the home server.
56  fr_ipaddr_t src_ipaddr; //!< IP we open our socket on.
57  uint16_t dst_port; //!< Port of the home server.
58  char const *secret; //!< Shared secret.
59 
60  char const *interface; //!< Interface to bind to.
61 
62  uint32_t recv_buff; //!< How big the kernel's receive buffer should be.
63  uint32_t send_buff; //!< How big the kernel's send buffer should be.
64 
65  uint32_t max_packet_size; //!< Maximum packet size.
66  uint16_t max_send_coalesce; //!< Maximum number of packets to coalesce into one mmsg call.
67 
68  bool recv_buff_is_set; //!< Whether we were provided with a recv_buf
69  bool send_buff_is_set; //!< Whether we were provided with a send_buf
70  bool replicate; //!< Copied from parent->replicate
71 
73 
74  trunk_conf_t trunk_conf; //!< trunk configuration
76 
77 typedef struct {
78  fr_event_list_t *el; //!< Event list.
79 
80  rlm_radius_udp_t const *inst; //!< our instance
81 
82  trunk_t *trunk; //!< trunk handler
83 } udp_thread_t;
84 
85 typedef struct {
87  rlm_rcode_t rcode; //!< from the transport
88 } udp_result_t;
89 
90 typedef struct udp_request_s udp_request_t;
91 
92 typedef struct {
93  struct iovec out; //!< Describes buffer to send.
94  trunk_request_t *treq; //!< Used for signalling.
96 
97 /** Track the handle, which is tightly correlated with the FD
98  *
99  */
100 typedef struct {
101  char const *name; //!< From IP PORT to IP PORT.
102  char const *module_name; //!< the module that opened the connection
103 
104  int fd; //!< File descriptor.
105 
106  struct mmsghdr *mmsgvec; //!< Vector of inbound/outbound packets.
107  udp_coalesced_t *coalesced; //!< Outbound coalesced requests.
108 
109  size_t send_buff_actual; //!< What we believe the maximum SO_SNDBUF size to be.
110  ///< We don't try and encode more packet data than this
111  ///< in one go.
112 
113  rlm_radius_udp_t const *inst; //!< Our module instance.
115 
116  uint8_t last_id; //!< Used when replicating to ensure IDs are distributed
117  ///< evenly.
118 
119  uint32_t max_packet_size; //!< Our max packet size. may be different from the parent.
120 
121  fr_ipaddr_t src_ipaddr; //!< Source IP address. May be altered on bind
122  //!< to be the actual IP address packets will be
123  //!< sent on. This is why we can't use the inst
124  //!< src_ipaddr field.
125  uint16_t src_port; //!< Source port specific to this connection.
126 
127  uint8_t *buffer; //!< Receive buffer.
128  size_t buflen; //!< Receive buffer length.
129 
130  radius_track_t *tt; //!< RADIUS ID tracking structure.
131 
132  fr_time_t mrs_time; //!< Most recent sent time which had a reply.
133  fr_time_t last_reply; //!< When we last received a reply.
134  fr_time_t first_sent; //!< first time we sent a packet since going idle
135  fr_time_t last_sent; //!< last time we sent a packet.
136  fr_time_t last_idle; //!< last time we had nothing to do
137 
138  fr_event_timer_t const *zombie_ev; //!< Zombie timeout.
139 
140  bool status_checking; //!< whether we're doing status checks
141  udp_request_t *status_u; //!< for sending status check packets
142  udp_result_t *status_r; //!< for faking out status checks as real packets
144 } udp_handle_t;
145 
146 
147 /** Connect request_t to local tracking structure
148  *
149  */
151  uint32_t priority; //!< copied from request->async->priority
152  fr_time_t recv_time; //!< copied from request->async->recv_time
153 
154  uint32_t num_replies; //!< number of reply packets, sent is in retry.count
155 
156  bool synchronous; //!< cached from inst->parent->synchronous
157  bool require_message_authenticator; //!< saved from the original packet.
158  bool status_check; //!< is this packet a status check?
159 
160  fr_pair_list_t extra; //!< VPs for debugging, like Proxy-State.
161 
162  uint8_t code; //!< Packet code.
163  uint8_t id; //!< Last ID assigned to this packet.
164  uint8_t *packet; //!< Packet we write to the network.
165  size_t packet_len; //!< Length of the packet.
166 
167  radius_track_entry_t *rr; //!< ID tracking, resend count, etc.
168  fr_event_timer_t const *ev; //!< timer for retransmissions
169  fr_retry_t retry; //!< retransmission timers
170 };
171 
172 static const conf_parser_t module_config[] = {
173  { FR_CONF_OFFSET_TYPE_FLAGS("ipaddr", FR_TYPE_COMBO_IP_ADDR, 0, rlm_radius_udp_t, dst_ipaddr), },
174  { FR_CONF_OFFSET_TYPE_FLAGS("ipv4addr", FR_TYPE_IPV4_ADDR, 0, rlm_radius_udp_t, dst_ipaddr) },
175  { FR_CONF_OFFSET_TYPE_FLAGS("ipv6addr", FR_TYPE_IPV6_ADDR, 0, rlm_radius_udp_t, dst_ipaddr) },
176 
177  { FR_CONF_OFFSET("port", rlm_radius_udp_t, dst_port) },
178 
180 
181  { FR_CONF_OFFSET("interface", rlm_radius_udp_t, interface) },
182 
183  { FR_CONF_OFFSET_IS_SET("recv_buff", FR_TYPE_UINT32, 0, rlm_radius_udp_t, recv_buff) },
184  { FR_CONF_OFFSET_IS_SET("send_buff", FR_TYPE_UINT32, 0, rlm_radius_udp_t, send_buff) },
185 
186  { FR_CONF_OFFSET("max_packet_size", rlm_radius_udp_t, max_packet_size), .dflt = "4096" },
187  { FR_CONF_OFFSET("max_send_coalesce", rlm_radius_udp_t, max_send_coalesce), .dflt = "1024" },
188 
189  { FR_CONF_OFFSET_TYPE_FLAGS("src_ipaddr", FR_TYPE_COMBO_IP_ADDR, 0, rlm_radius_udp_t, src_ipaddr) },
190  { FR_CONF_OFFSET_TYPE_FLAGS("src_ipv4addr", FR_TYPE_IPV4_ADDR, 0, rlm_radius_udp_t, src_ipaddr) },
191  { FR_CONF_OFFSET_TYPE_FLAGS("src_ipv6addr", FR_TYPE_IPV6_ADDR, 0, rlm_radius_udp_t, src_ipaddr) },
192 
194 };
195 
196 static fr_dict_t const *dict_radius;
197 
200  { .out = &dict_radius, .proto = "radius" },
201  { NULL }
202 };
203 
215 
218  { .out = &attr_error_cause, .name = "Error-Cause", .type = FR_TYPE_UINT32, .dict = &dict_radius },
219  { .out = &attr_event_timestamp, .name = "Event-Timestamp", .type = FR_TYPE_DATE, .dict = &dict_radius},
220  { .out = &attr_extended_attribute_1, .name = "Extended-Attribute-1", .type = FR_TYPE_TLV, .dict = &dict_radius},
221  { .out = &attr_message_authenticator, .name = "Message-Authenticator", .type = FR_TYPE_OCTETS, .dict = &dict_radius},
222  { .out = &attr_eap_message, .name = "EAP-Message", .type = FR_TYPE_OCTETS, .dict = &dict_radius},
223  { .out = &attr_nas_identifier, .name = "NAS-Identifier", .type = FR_TYPE_STRING, .dict = &dict_radius},
224  { .out = &attr_original_packet_code, .name = "Extended-Attribute-1.Original-Packet-Code", .type = FR_TYPE_UINT32, .dict = &dict_radius},
225  { .out = &attr_proxy_state, .name = "Proxy-State", .type = FR_TYPE_OCTETS, .dict = &dict_radius},
226  { .out = &attr_response_length, .name = "Extended-Attribute-1.Response-Length", .type = FR_TYPE_UINT32, .dict = &dict_radius },
227  { .out = &attr_user_password, .name = "User-Password", .type = FR_TYPE_STRING, .dict = &dict_radius},
228  { .out = &attr_packet_type, .name = "Packet-Type", .type = FR_TYPE_UINT32, .dict = &dict_radius },
229  { NULL }
230 };
231 
232 /** Turn a reply code into a module rcode;
233  *
234  */
239 
241 
244 
247 
249 };
250 
252  UNUSED int flags, void *uctx);
253 
254 static int encode(rlm_radius_udp_t const *inst, request_t *request, udp_request_t *u, uint8_t id);
255 
256 static decode_fail_t decode(TALLOC_CTX *ctx, fr_pair_list_t *reply, uint8_t *response_code,
257  udp_handle_t *h, request_t *request, udp_request_t *u,
258  uint8_t const request_authenticator[static RADIUS_AUTH_VECTOR_LENGTH],
259  uint8_t *data, size_t data_len);
260 
262 
263 #ifndef NDEBUG
264 /** Log additional information about a tracking entry
265  *
266  * @param[in] te Tracking entry we're logging information for.
267  * @param[in] log destination.
268  * @param[in] log_type Type of log message.
269  * @param[in] file the logging request was made in.
270  * @param[in] line logging request was made on.
271  */
272 static void udp_tracking_entry_log(fr_log_t const *log, fr_log_type_t log_type, char const *file, int line,
274 {
275  request_t *request;
276 
277  if (!te->request) return; /* Free entry */
278 
279  request = talloc_get_type_abort(te->request, request_t);
280 
281  fr_log(log, log_type, file, line, "request %s, allocated %s:%u", request->name,
282  request->alloc_file, request->alloc_line);
283 
284  trunk_request_state_log(log, log_type, file, line, talloc_get_type_abort(te->uctx, trunk_request_t));
285 }
286 #endif
287 
288 /** Clear out any connection specific resources from a udp request
289  *
290  */
292 {
293  TALLOC_FREE(u->packet);
294  fr_pair_list_init(&u->extra); /* Freed with packet */
295 
296  /*
297  * Can have packet put no u->rr
298  * if this is part of a pre-trunk status check.
299  */
300  if (u->rr) radius_track_entry_release(&u->rr);
301 }
302 
303 /** Reset a status_check packet, ready to reuse
304  *
305  */
307 {
308  fr_assert(u->status_check == true);
309 
310  h->status_checking = false;
311  u->num_replies = 0; /* Reset */
312  u->retry.start = fr_time_wrap(0);
313 
314  if (u->ev) (void) fr_event_timer_delete(&u->ev);
315 
317 }
318 
319 /*
320  * Status-Server checks. Manually build the packet, and
321  * all of its associated glue.
322  */
323 static void CC_HINT(nonnull) status_check_alloc(udp_handle_t *h)
324 {
325  udp_request_t *u;
326  request_t *request;
327  rlm_radius_udp_t const *inst = h->inst;
328  map_t *map = NULL;
329 
330  fr_assert(!h->status_u && !h->status_r && !h->status_request);
331 
332  u = talloc_zero(h, udp_request_t);
334 
335  /*
336  * Status checks are prioritized over any other packet
337  */
338  u->priority = ~(uint32_t) 0;
339  u->status_check = true;
340 
341  /*
342  * Allocate outside of the free list.
343  * There appears to be an issue where
344  * the thread destructor runs too
345  * early, and frees the freelist's
346  * head before the module destructor
347  * runs.
348  */
349  request = request_local_alloc_external(u, NULL);
350  request->async = talloc_zero(request, fr_async_t);
351  talloc_const_free(request->name);
352  request->name = talloc_strdup(request, h->module_name);
353 
354  request->packet = fr_packet_alloc(request, false);
355  request->reply = fr_packet_alloc(request, false);
356 
357  /*
358  * Create the VPs, and ignore any errors
359  * creating them.
360  */
361  while ((map = map_list_next(&inst->parent->status_check_map, map))) {
362  /*
363  * Skip things which aren't attributes.
364  */
365  if (!tmpl_is_attr(map->lhs)) continue;
366 
367  /*
368  * Ignore internal attributes.
369  */
370  if (tmpl_attr_tail_da(map->lhs)->flags.internal) continue;
371 
372  /*
373  * Ignore signalling attributes. They shouldn't exist.
374  */
375  if ((tmpl_attr_tail_da(map->lhs) == attr_proxy_state) ||
376  (tmpl_attr_tail_da(map->lhs) == attr_message_authenticator)) continue;
377 
378  /*
379  * Allow passwords only in Access-Request packets.
380  */
381  if ((inst->parent->status_check != FR_RADIUS_CODE_ACCESS_REQUEST) &&
382  (tmpl_attr_tail_da(map->lhs) == attr_user_password)) continue;
383 
384  (void) map_to_request(request, map, map_to_vp, NULL);
385  }
386 
387  /*
388  * Ensure that there's a NAS-Identifier, if one wasn't
389  * already added.
390  */
391  if (!fr_pair_find_by_da(&request->request_pairs, NULL, attr_nas_identifier)) {
392  fr_pair_t *vp;
393 
395  fr_pair_value_strdup(vp, "status check - are you alive?", false);
396  }
397 
398  /*
399  * Always add an Event-Timestamp, which will be the time
400  * at which the first packet is sent. Or for
401  * Status-Server, the time of the current packet.
402  */
403  if (!fr_pair_find_by_da(&request->request_pairs, NULL, attr_event_timestamp)) {
405  }
406 
407  /*
408  * Initialize the request IO ctx. Note that we don't set
409  * destructors.
410  */
411  u->code = inst->parent->status_check;
412  request->packet->code = u->code;
413 
414  DEBUG3("%s - Status check packet type will be %s", h->module_name, fr_radius_packet_name[u->code]);
415  log_request_pair_list(L_DBG_LVL_3, request, NULL, &request->request_pairs, NULL);
416 
417  MEM(h->status_r = talloc_zero(request, udp_result_t));
418  h->status_u = u;
419  h->status_request = request;
420 }
421 
422 /** Connection errored
423  *
424  * We were signalled by the event loop that a fatal error occurred on this connection.
425  *
426  * @param[in] el The event list signalling.
427  * @param[in] fd that errored.
428  * @param[in] flags El flags.
429  * @param[in] fd_errno The nature of the error.
430  * @param[in] uctx The trunk connection handle (tconn).
431  */
432 static void conn_error_status_check(UNUSED fr_event_list_t *el, UNUSED int fd, UNUSED int flags, int fd_errno, void *uctx)
433 {
434  connection_t *conn = talloc_get_type_abort(uctx, connection_t);
435  udp_handle_t *h;
436 
437  /*
438  * Connection must be in the connecting state when this fires
439  */
440  fr_assert(conn->state == CONNECTION_STATE_CONNECTING);
441 
442  h = talloc_get_type_abort(conn->h, udp_handle_t);
443 
444  ERROR("%s - Connection %s failed: %s", h->module_name, h->name, fr_syserror(fd_errno));
445 
447 }
448 
449 /** Status check timer when opening the connection for the first time.
450  *
451  * Setup retries, or fail the connection.
452  */
454 {
455  connection_t *conn = talloc_get_type_abort(uctx, connection_t);
456  udp_handle_t *h;
457  udp_request_t *u;
458 
459  /*
460  * Connection must be in the connecting state when this fires
461  */
462  fr_assert(conn->state == CONNECTION_STATE_CONNECTING);
463 
464  h = talloc_get_type_abort(conn->h, udp_handle_t);
465  u = h->status_u;
466 
467  /*
468  * We're only interested in contiguous, good, replies.
469  */
470  u->num_replies = 0;
471 
472  switch (fr_retry_next(&u->retry, now)) {
473  case FR_RETRY_MRD:
474  DEBUG("%s - Reached maximum_retransmit_duration (%pVs > %pVs), failing status checks",
477  goto fail;
478 
479  case FR_RETRY_MRC:
480  DEBUG("%s - Reached maximum_retransmit_count (%u > %u), failing status checks",
481  h->module_name, u->retry.count, u->retry.config->mrc);
482  fail:
484  return;
485 
486  case FR_RETRY_CONTINUE:
487  if (fr_event_fd_insert(h, NULL, el, h->fd, conn_writable_status_check, NULL,
488  conn_error_status_check, conn) < 0) {
489  PERROR("%s - Failed inserting FD event", h->module_name);
491  }
492  return;
493  }
494 
495  fr_assert(0);
496 }
497 
498 /** Send the next status check packet
499  *
500  */
502 {
503  connection_t *conn = talloc_get_type_abort(uctx, connection_t);
504  udp_handle_t *h = talloc_get_type_abort(conn->h, udp_handle_t);
505 
506  if (fr_event_fd_insert(h, NULL, el, h->fd, conn_writable_status_check, NULL, conn_error_status_check, conn) < 0) {
507  PERROR("%s - Failed inserting FD event", h->module_name);
509  }
510 }
511 
512 /** Read the incoming status-check response. If it's correct mark the connection as connected
513  *
514  */
515 static void conn_readable_status_check(fr_event_list_t *el, UNUSED int fd, UNUSED int flags, void *uctx)
516 {
517  connection_t *conn = talloc_get_type_abort(uctx, connection_t);
518  udp_handle_t *h = talloc_get_type_abort(conn->h, udp_handle_t);
519  trunk_t *trunk = h->thread->trunk;
520  rlm_radius_t const *inst = h->inst->parent;
521  udp_request_t *u = h->status_u;
522  ssize_t slen;
523  fr_pair_list_t reply;
524  uint8_t code = 0;
525 
526  fr_pair_list_init(&reply);
527  slen = read(h->fd, h->buffer, h->buflen);
528  if (slen == 0) return;
529 
530  if (slen < 0) {
531  switch (errno) {
532 #if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN)
533  case EWOULDBLOCK:
534 #endif
535  case EAGAIN:
536  case EINTR:
537  return; /* Wait to be signalled again */
538 
539  default:
540  break;
541  }
542 
543  ERROR("%s - Failed reading response from socket: %s",
544  h->module_name, fr_syserror(errno));
546  return;
547  }
548 
549  /*
550  * Where we just return in this function, we're letting
551  * the response timer take care of progressing the
552  * connection attempt.
553  */
554  if (slen < RADIUS_HEADER_LENGTH) {
555  ERROR("%s - Packet too short, expected at least %zu bytes got %zd bytes",
556  h->module_name, (size_t)RADIUS_HEADER_LENGTH, slen);
557  return;
558  }
559 
560  if (u->id != h->buffer[1]) {
561  ERROR("%s - Received response with incorrect or expired ID. Expected %u, got %u",
562  h->module_name, u->id, h->buffer[1]);
563  return;
564  }
565 
566  if (!check(h, &slen)) return;
567 
568  if (decode(h, &reply, &code,
570  h->buffer, slen) != DECODE_FAIL_NONE) return;
571 
572  fr_pair_list_free(&reply); /* FIXME - Do something with these... */
573 
574  /*
575  * Process the error, and count this as a success.
576  * This is usually used for dynamic configuration
577  * on startup.
578  */
579  if (code == FR_RADIUS_CODE_PROTOCOL_ERROR) protocol_error_reply(u, NULL, h);
580 
581  /*
582  * Last trunk event was a failure, be more careful about
583  * bringing up the connection (require multiple responses).
584  */
585  if ((fr_time_gt(trunk->last_failed, fr_time_wrap(0)) && (fr_time_gt(trunk->last_failed, trunk->last_connected))) &&
586  (u->num_replies < inst->num_answers_to_alive)) {
587  /*
588  * Leave the timer in place. This timer is BOTH when we
589  * give up on the current status check, AND when we send
590  * the next status check.
591  */
592  DEBUG("%s - Received %u / %u replies for status check, on connection - %s",
593  h->module_name, u->num_replies, inst->num_answers_to_alive, h->name);
594  DEBUG("%s - Next status check packet will be in %pVs",
596 
597  /*
598  * Set the timer for the next retransmit.
599  */
600  if (fr_event_timer_at(h, el, &u->ev, u->retry.next, conn_status_check_again, conn) < 0) {
602  }
603  return;
604  }
605 
606  /*
607  * It's alive!
608  */
609  status_check_reset(h, u);
610 
611  DEBUG("%s - Connection open - %s", h->module_name, h->name);
612 
614 }
615 
616 /** Send our status-check packet as soon as the connection becomes writable
617  *
618  */
619 static void conn_writable_status_check(fr_event_list_t *el, UNUSED int fd, UNUSED int flags, void *uctx)
620 {
621  connection_t *conn = talloc_get_type_abort(uctx, connection_t);
622  udp_handle_t *h = talloc_get_type_abort(conn->h, udp_handle_t);
623  udp_request_t *u = h->status_u;
624  ssize_t slen;
625 
626  if (fr_time_eq(u->retry.start, fr_time_wrap(0))) {
627  u->id = fr_rand() & 0xff; /* We don't care what the value is here */
628  h->status_checking = true; /* Ensure this is valid */
629  fr_retry_init(&u->retry, fr_time(), &h->inst->parent->retry[u->code]);
630 
631  /*
632  * Status checks can never be retransmitted
633  * So increment the ID here.
634  */
635  } else {
637  u->id++;
638  }
639 
640  DEBUG("%s - Sending %s ID %d over connection %s",
641  h->module_name, fr_radius_packet_name[u->code], u->id, h->name);
642 
643  if (encode(h->inst, h->status_request, u, u->id) < 0) {
644  fail:
646  return;
647  }
648  DEBUG3("Encoded packet");
649  HEXDUMP3(u->packet, u->packet_len, NULL);
650 
651  slen = write(h->fd, u->packet, u->packet_len);
652  if (slen < 0) {
653  ERROR("%s - Failed sending %s ID %d length %ld over connection %s: %s",
654  h->module_name, fr_radius_packet_name[u->code], u->id, u->packet_len, h->name, fr_syserror(errno));
655  goto fail;
656  }
657  fr_assert((size_t)slen == u->packet_len);
658 
659  /*
660  * Switch to waiting on read and insert the event
661  * for the response timeout.
662  */
663  if (fr_event_fd_insert(h, NULL, conn->el, h->fd, conn_readable_status_check, NULL, conn_error_status_check, conn) < 0) {
664  PERROR("%s - Failed inserting FD event", h->module_name);
665  goto fail;
666  }
667 
668  DEBUG("%s - %s request. Expecting response within %pVs",
669  h->module_name, (u->retry.count == 1) ? "Originated" : "Retransmitted",
671 
672  if (fr_event_timer_at(u, el, &u->ev, u->retry.next, conn_status_check_timeout, conn) < 0) {
673  PERROR("%s - Failed inserting timer event", h->module_name);
674  goto fail;
675  }
676 }
677 
678 /** Free a connection handle, closing associated resources
679  *
680  */
682 {
683  fr_assert(h->fd >= 0);
684 
686 
688 
689  if (shutdown(h->fd, SHUT_RDWR) < 0) {
690  DEBUG3("%s - Failed shutting down connection %s: %s",
691  h->module_name, h->name, fr_syserror(errno));
692  }
693 
694  if (close(h->fd) < 0) {
695  DEBUG3("%s - Failed closing connection %s: %s",
696  h->module_name, h->name, fr_syserror(errno));
697  }
698 
699  h->fd = -1;
700 
701  DEBUG("%s - Connection closed - %s", h->module_name, h->name);
702 
703  return 0;
704 }
705 
706 /** Initialise a new outbound connection
707  *
708  * @param[out] h_out Where to write the new file descriptor.
709  * @param[in] conn to initialise.
710  * @param[in] uctx A #udp_thread_t
711  */
712 CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function*/
713 static connection_state_t conn_init(void **h_out, connection_t *conn, void *uctx)
714 {
715  int fd;
716  udp_handle_t *h;
717  udp_thread_t *thread = talloc_get_type_abort(uctx, udp_thread_t);
718  uint16_t i;
719 
720  MEM(h = talloc_zero(conn, udp_handle_t));
721  h->thread = thread;
722  h->inst = thread->inst;
723  h->module_name = h->inst->parent->name;
724  h->src_ipaddr = h->inst->src_ipaddr;
725  h->src_port = 0;
727  h->last_idle = fr_time();
728 
729  /*
730  * mmsgvec is pre-populated with pointers
731  * to the iovec structs in coalesced, so we
732  * just need to setup the iovec, and pass how
733  * many messages we want to send to sendmmsg.
734  */
735  h->mmsgvec = talloc_zero_array(h, struct mmsghdr, h->inst->max_send_coalesce);
736  h->coalesced = talloc_zero_array(h, udp_coalesced_t, h->inst->max_send_coalesce);
737  for (i = 0; i < h->inst->max_send_coalesce; i++) {
738  h->mmsgvec[i].msg_hdr.msg_iov = &h->coalesced[i].out;
739  h->mmsgvec[i].msg_hdr.msg_iovlen = 1;
740  }
741 
742  MEM(h->buffer = talloc_array(h, uint8_t, h->max_packet_size));
743  h->buflen = h->max_packet_size;
744 
745  if (!h->inst->replicate) MEM(h->tt = radius_track_alloc(h));
746 
747  /*
748  * Open the outgoing socket.
749  */
751  &h->inst->dst_ipaddr, h->inst->dst_port, true);
752  if (fd < 0) {
753  PERROR("%s - Failed opening socket", h->module_name);
754  fail:
755  talloc_free(h);
757  }
758 
759  /*
760  * Set the connection name.
761  */
762  h->name = fr_asprintf(h, "proto udp local %pV port %u remote %pV port %u",
765 
766  talloc_set_destructor(h, _udp_handle_free);
767 
768 #ifdef SO_RCVBUF
769  if (h->inst->recv_buff_is_set) {
770  int opt;
771 
772  opt = h->inst->recv_buff;
773  if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(int)) < 0) {
774  WARN("%s - Failed setting 'SO_RCVBUF': %s", h->module_name, fr_syserror(errno));
775  }
776  }
777 #endif
778 
779 #ifdef SO_SNDBUF
780  {
781  int opt;
782  socklen_t socklen = sizeof(int);
783 
784  if (h->inst->send_buff_is_set) {
785  opt = h->inst->send_buff;
786  if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &opt, sizeof(int)) < 0) {
787  WARN("%s - Failed setting 'SO_SNDBUF', write performance may be sub-optimal: %s",
788  h->module_name, fr_syserror(errno));
789  }
790  }
791 
792  if (getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &opt, &socklen) < 0) {
793  WARN("%s - Failed getting 'SO_SNDBUF', write performance may be sub-optimal: %s",
794  h->module_name, fr_syserror(errno));
795 
796  /*
797  * This controls how many packets we attempt
798  * to send at once. Nothing bad happens if
799  * we get it wrong, but the user may see
800  * ENOBUFS errors at high packet rates.
801  */
804 
805  WARN("%s - Max coalesced outbound data will be %zu bytes", h->module_name,
806  h->send_buff_actual);
807  } else {
808 #ifdef __linux__
809  /*
810  * Linux doubles the buffer when you set it
811  * to account for "overhead".
812  */
813  h->send_buff_actual = ((size_t)opt) / 2;
814 #else
815  h->send_buff_actual = (size_t)opt;
816 #endif
817  }
818  }
819 #else
821  h->inst_send_buff : h->max_packet_size * h->inst->max_send_coalesce;
822 
823  WARN("%s - Modifying 'SO_SNDBUF' value is not supported on this system, "
824  "write performance may be sub-optimal", h->module_name);
825  WARN("%s - Max coalesced outbound data will be %zu bytes", h->module_name, h->inst->send_buff_actual);
826 #endif
827 
828  h->fd = fd;
829 
830  /*
831  * If we're doing status checks, then we want at least
832  * one positive response before signalling that the
833  * connection is open.
834  *
835  * To do this we install special I/O handlers that
836  * only signal the connection as open once we get a
837  * status-check response.
838  */
839  if (h->inst->parent->status_check) {
841 
842  /*
843  * Start status checking.
844  *
845  * If we've had no recent failures we need exactly
846  * one response to bring the connection online,
847  * otherwise we need inst->num_answers_to_alive
848  */
849  if (fr_event_fd_insert(h, NULL, conn->el, h->fd, NULL,
851  /*
852  * If we're not doing status-checks, signal the connection
853  * as open as soon as it becomes writable.
854  */
855  } else {
856  connection_signal_on_fd(conn, fd);
857  }
858 
859  *h_out = h;
860 
861  // @todo - initialize the tracking memory, etc.
862  // i.e. histograms (or hyperloglog) of packets, so we can see
863  // which connections / home servers are fast / slow.
864 
866 }
867 
868 /** Shutdown/close a file descriptor
869  *
870  */
871 static void conn_close(UNUSED fr_event_list_t *el, void *handle, UNUSED void *uctx)
872 {
873  udp_handle_t *h = talloc_get_type_abort(handle, udp_handle_t);
874 
875  /*
876  * There's tracking entries still allocated
877  * this is bad, they should have all been
878  * released.
879  */
880  if (h->tt && (h->tt->num_requests != 0)) {
881 #ifndef NDEBUG
883 #endif
884  fr_assert_fail("%u tracking entries still allocated at conn close", h->tt->num_requests);
885  }
886 
887  DEBUG4("Freeing rlm_radius_udp handle %p", handle);
888 
889  talloc_free(h);
890 }
891 
892 /** Connection failed
893  *
894  * @param[in] handle of connection that failed.
895  * @param[in] state the connection was in when it failed.
896  * @param[in] uctx UNUSED.
897  */
898 static connection_state_t conn_failed(void *handle, connection_state_t state, UNUSED void *uctx)
899 {
900  switch (state) {
901  /*
902  * If the connection was connected when it failed,
903  * we need to handle any outstanding packets and
904  * timer events before reconnecting.
905  */
907  {
908  udp_handle_t *h = talloc_get_type_abort(handle, udp_handle_t); /* h only available if connected */
909 
910  /*
911  * Reset the Status-Server checks.
912  */
913  if (h->status_u && h->status_u->ev) (void) fr_event_timer_delete(&h->status_u->ev);
914  }
915  break;
916 
917  default:
918  break;
919  }
920 
921  return CONNECTION_STATE_INIT;
922 }
923 
924 CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function*/
925 static connection_t *thread_conn_alloc(trunk_connection_t *tconn, fr_event_list_t *el,
926  connection_conf_t const *conf,
927  char const *log_prefix, void *uctx)
928 {
929  connection_t *conn;
930  udp_thread_t *thread = talloc_get_type_abort(uctx, udp_thread_t);
931 
932  conn = connection_alloc(tconn, el,
934  .init = conn_init,
935  .close = conn_close,
936  .failed = conn_failed
937  },
938  conf,
939  log_prefix,
940  thread);
941  if (!conn) {
942  PERROR("%s - Failed allocating state handler for new connection", thread->inst->parent->name);
943  return NULL;
944  }
945 
946  return conn;
947 }
948 
949 /** Read and discard data
950  *
951  */
952 static void conn_discard(UNUSED fr_event_list_t *el, int fd, UNUSED int flags, void *uctx)
953 {
954  trunk_connection_t *tconn = talloc_get_type_abort(uctx, trunk_connection_t);
955  udp_handle_t *h = talloc_get_type_abort(tconn->conn->h, udp_handle_t);
956  uint8_t buffer[4096];
957  ssize_t slen;
958 
959  while ((slen = read(fd, buffer, sizeof(buffer))) > 0);
960 
961  if (slen < 0) {
962  switch (errno) {
963  case EBADF:
964  case ECONNRESET:
965  case ENOTCONN:
966  case ETIMEDOUT:
967  ERROR("%s - Failed draining socket: %s", h->module_name, fr_syserror(errno));
969  break;
970 
971  default:
972  break;
973  }
974  }
975 }
976 
977 /** Connection errored
978  *
979  * We were signalled by the event loop that a fatal error occurred on this connection.
980  *
981  * @param[in] el The event list signalling.
982  * @param[in] fd that errored.
983  * @param[in] flags El flags.
984  * @param[in] fd_errno The nature of the error.
985  * @param[in] uctx The trunk connection handle (tconn).
986  */
987 static void conn_error(UNUSED fr_event_list_t *el, UNUSED int fd, UNUSED int flags, int fd_errno, void *uctx)
988 {
989  trunk_connection_t *tconn = talloc_get_type_abort(uctx, trunk_connection_t);
990  connection_t *conn = tconn->conn;
991  udp_handle_t *h = talloc_get_type_abort(conn->h, udp_handle_t);
992 
993  ERROR("%s - Connection %s failed: %s", h->module_name, h->name, fr_syserror(fd_errno));
994 
996 }
997 
998 CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function*/
999 static void thread_conn_notify(trunk_connection_t *tconn, connection_t *conn,
1001  trunk_connection_event_t notify_on, UNUSED void *uctx)
1002 {
1003  udp_handle_t *h = talloc_get_type_abort(conn->h, udp_handle_t);
1004  fr_event_fd_cb_t read_fn = NULL;
1005  fr_event_fd_cb_t write_fn = NULL;
1006 
1007  switch (notify_on) {
1008  /*
1009  * We may have sent multiple requests to the
1010  * other end, so it might be sending us multiple
1011  * replies. We want to drain the socket, instead
1012  * of letting the packets sit in the UDP receive
1013  * queue.
1014  */
1015  case TRUNK_CONN_EVENT_NONE:
1016  read_fn = conn_discard;
1017  break;
1018 
1019  case TRUNK_CONN_EVENT_READ:
1021  break;
1022 
1025  break;
1026 
1027  case TRUNK_CONN_EVENT_BOTH:
1030  break;
1031 
1032  }
1033 
1034  if (fr_event_fd_insert(h, NULL, el, h->fd,
1035  read_fn,
1036  write_fn,
1037  conn_error,
1038  tconn) < 0) {
1039  PERROR("%s - Failed inserting FD event", h->module_name);
1040 
1041  /*
1042  * May free the connection!
1043  */
1045  }
1046 }
1047 
1048 /** A special version of the trunk/event loop glue function which always discards incoming data
1049  *
1050  */
1051 CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function*/
1052 static void thread_conn_notify_replicate(trunk_connection_t *tconn, connection_t *conn,
1054  trunk_connection_event_t notify_on, UNUSED void *uctx)
1055 {
1056  udp_handle_t *h = talloc_get_type_abort(conn->h, udp_handle_t);
1057  fr_event_fd_cb_t read_fn = NULL;
1058  fr_event_fd_cb_t write_fn = NULL;
1059 
1060  switch (notify_on) {
1061  case TRUNK_CONN_EVENT_NONE:
1062  read_fn = conn_discard;
1063  write_fn = NULL;
1064  break;
1065 
1066  case TRUNK_CONN_EVENT_READ:
1067  read_fn = conn_discard;
1068  break;
1069 
1070  case TRUNK_CONN_EVENT_BOTH:
1072  read_fn = conn_discard;
1074  break;
1075  }
1076 
1077  if (fr_event_fd_insert(h, NULL, el, h->fd,
1078  read_fn,
1079  write_fn,
1080  conn_error,
1081  tconn) < 0) {
1082  PERROR("%s - Failed inserting FD event", h->module_name);
1083 
1084  /*
1085  * May free the connection!
1086  */
1088  }
1089 }
1090 
1091 /*
1092  * Return negative numbers to put 'a' at the top of the heap.
1093  * Return positive numbers to put 'b' at the top of the heap.
1094  *
1095  * We want the value with the lowest timestamp to be prioritized at
1096  * the top of the heap.
1097  */
1098 static int8_t request_prioritise(void const *one, void const *two)
1099 {
1100  udp_request_t const *a = one;
1101  udp_request_t const *b = two;
1102  int8_t ret;
1103 
1104  // @todo - prioritize packets if there's a state?
1105 
1106  /*
1107  * Prioritise status check packets
1108  */
1109  ret = (b->status_check - a->status_check);
1110  if (ret != 0) return ret;
1111 
1112  /*
1113  * Larger priority is more important.
1114  */
1115  ret = CMP(a->priority, b->priority);
1116  if (ret != 0) return ret;
1117 
1118  /*
1119  * Smaller timestamp (i.e. earlier) is more important.
1120  */
1122 }
1123 
1124 /** Decode response packet data, extracting relevant information and validating the packet
1125  *
1126  * @param[in] ctx to allocate pairs in.
1127  * @param[out] reply Pointer to head of pair list to add reply attributes to.
1128  * @param[out] response_code The type of response packet.
1129  * @param[in] h connection handle.
1130  * @param[in] request the request.
1131  * @param[in] u UDP request.
1132  * @param[in] request_authenticator from the original request.
1133  * @param[in] data to decode.
1134  * @param[in] data_len Length of input data.
1135  * @return
1136  * - DECODE_FAIL_NONE on success.
1137  * - DECODE_FAIL_* on failure.
1138  */
1139 static decode_fail_t decode(TALLOC_CTX *ctx, fr_pair_list_t *reply, uint8_t *response_code,
1140  udp_handle_t *h, request_t *request, udp_request_t *u,
1141  uint8_t const request_authenticator[static RADIUS_AUTH_VECTOR_LENGTH],
1142  uint8_t *data, size_t data_len)
1143 {
1145  rlm_radius_t const *parent = inst->parent;
1146  uint8_t code;
1147  fr_radius_decode_ctx_t decode_ctx;
1148 
1149  *response_code = 0; /* Initialise to keep the rest of the code happy */
1150 
1151  RHEXDUMP3(data, data_len, "Read packet");
1152 
1153  decode_ctx = (fr_radius_decode_ctx_t) {
1154  .common = &inst->common_ctx,
1155  .request_code = u->code,
1156  .request_authenticator = request_authenticator,
1157  .tmp_ctx = talloc(ctx, uint8_t),
1158  .end = data + data_len,
1159  .verify = true,
1160  .require_message_authenticator = ((*(parent->received_message_authenticator) & parent->require_message_authenticator) |
1161  (parent->require_message_authenticator & FR_RADIUS_REQUIRE_MA_YES)) > 0
1162  };
1163 
1164  if (fr_radius_decode(ctx, reply, data, data_len, &decode_ctx) < 0) {
1165  talloc_free(decode_ctx.tmp_ctx);
1166  RPEDEBUG("Failed reading packet");
1167  return DECODE_FAIL_UNKNOWN;
1168  }
1169  talloc_free(decode_ctx.tmp_ctx);
1170 
1171  code = data[0];
1172 
1173  RDEBUG("Received %s ID %d length %ld reply packet on connection %s",
1174  fr_radius_packet_name[code], data[1], data_len, h->name);
1175  log_request_pair_list(L_DBG_LVL_2, request, NULL, reply, NULL);
1176 
1177  /*
1178  * This code is for BlastRADIUS mitigation.
1179  *
1180  * The scenario where this applies is where we send Message-Authenticator
1181  * but the home server doesn't support it or require it, in which case
1182  * the response can be manipulated by an attacker.
1183  */
1184  if (u->code == FR_RADIUS_CODE_ACCESS_REQUEST) {
1185  if ((parent->require_message_authenticator == FR_RADIUS_REQUIRE_MA_AUTO) &&
1186  !*(parent->received_message_authenticator) &&
1187  fr_pair_find_by_da(&request->request_pairs, NULL, attr_message_authenticator) &&
1188  !fr_pair_find_by_da(&request->request_pairs, NULL, attr_eap_message)) {
1189  RINFO("Packet contained a valid Message-Authenticator. Setting \"require_message_authenticator = yes\"");
1190  *(parent->received_message_authenticator) = true;
1191  }
1192  }
1193 
1194  *response_code = code;
1195 
1196  /*
1197  * Record the fact we've seen a response
1198  */
1199  u->num_replies++;
1200 
1201  /*
1202  * Fixup retry times
1203  */
1204  if (fr_time_gt(u->retry.start, h->mrs_time)) h->mrs_time = u->retry.start;
1205 
1206  return DECODE_FAIL_NONE;
1207 }
1208 
1209 static int encode(rlm_radius_udp_t const *inst, request_t *request, udp_request_t *u, uint8_t id)
1210 {
1211  ssize_t packet_len;
1213 
1214  fr_assert(inst->parent->allowed[u->code]);
1215  fr_assert(!u->packet);
1216 
1217  /*
1218  * This is essentially free, as this memory was
1219  * pre-allocated as part of the treq.
1220  */
1221  u->packet_len = inst->max_packet_size;
1222  MEM(u->packet = talloc_array(u, uint8_t, u->packet_len));
1223 
1224  /*
1225  * We should have at minimum 64-byte packets, so don't
1226  * bother doing run-time checks here.
1227  */
1228  fr_assert(u->packet_len >= (size_t) RADIUS_HEADER_LENGTH);
1229 
1231  .common = &inst->common_ctx,
1232  .rand_ctx = (fr_fast_rand_t) {
1233  .a = fr_rand(),
1234  .b = fr_rand(),
1235  },
1236  .code = u->code,
1237  .id = id,
1238  .add_proxy_state = !inst->parent->originate,
1239  };
1240 
1241  /*
1242  * If we're sending a status check packet, update any
1243  * necessary timestamps. Also, don't add Proxy-State, as
1244  * we're originating the packet.
1245  */
1246  if (u->status_check) {
1247  fr_pair_t *vp;
1248 
1249  vp = fr_pair_find_by_da(&request->request_pairs, NULL, attr_event_timestamp);
1250  if (vp) vp->vp_date = fr_time_to_unix_time(u->retry.updated);
1251 
1252  encode_ctx.add_proxy_state = false;
1253  }
1254 
1255  /*
1256  * Encode it, leaving room for Proxy-State if necessary.
1257  */
1258  packet_len = fr_radius_encode(&FR_DBUFF_TMP(u->packet, u->packet_len),
1259  &request->request_pairs, &encode_ctx);
1260  if (fr_pair_encode_is_error(packet_len)) {
1261  RPERROR("Failed encoding packet");
1262 
1263  error:
1264  TALLOC_FREE(u->packet);
1265  return -1;
1266  }
1267 
1268  if (packet_len < 0) {
1269  size_t have;
1270  size_t need;
1271 
1272  have = u->packet_len;
1273  need = have - packet_len;
1274 
1275  if (need > RADIUS_MAX_PACKET_SIZE) {
1276  RERROR("Failed encoding packet. Have %zu bytes of buffer, need %zu bytes",
1277  have, need);
1278  } else {
1279  RERROR("Failed encoding packet. Have %zu bytes of buffer, need %zu bytes. "
1280  "Increase 'max_packet_size'", have, need);
1281  }
1282 
1283  goto error;
1284  }
1285  /*
1286  * The encoded packet should NOT over-run the input buffer.
1287  */
1288  fr_assert((size_t) packet_len <= u->packet_len);
1289 
1290  /*
1291  * Add Proxy-State to the tail end of the packet.
1292  *
1293  * We need to add it here, and NOT in
1294  * request->request_pairs, because multiple modules
1295  * may be sending the packets at the same time.
1296  */
1297  if (encode_ctx.add_proxy_state) {
1298  fr_pair_t *vp;
1299 
1301  fr_pair_value_memdup(vp, (uint8_t const *) &inst->common_ctx.proxy_state, sizeof(inst->common_ctx.proxy_state), false);
1302  fr_pair_append(&u->extra, vp);
1303  }
1304 
1305  /*
1306  * Update our version of the packet length.
1307  */
1308  u->packet_len = packet_len;
1309 
1310  /*
1311  * Now that we're done mangling the packet, sign it.
1312  */
1313  if (fr_radius_sign(u->packet, NULL, (uint8_t const *) inst->secret,
1314  talloc_array_length(inst->secret) - 1) < 0) {
1315  RERROR("Failed signing packet");
1316  goto error;
1317  }
1318 
1319  return 0;
1320 }
1321 
1322 
1323 /** Revive a connection after "revive_interval"
1324  *
1325  */
1327 {
1328  trunk_connection_t *tconn = talloc_get_type_abort(uctx, trunk_connection_t);
1329  udp_handle_t *h = talloc_get_type_abort(tconn->conn->h, udp_handle_t);
1330 
1331  INFO("%s - Reviving connection %s", h->module_name, h->name);
1333 }
1334 
1335 /** Mark a connection dead after "zombie_interval"
1336  *
1337  */
1339 {
1340  trunk_connection_t *tconn = talloc_get_type_abort(uctx, trunk_connection_t);
1341  udp_handle_t *h = talloc_get_type_abort(tconn->conn->h, udp_handle_t);
1342 
1343  INFO("%s - No replies during 'zombie_period', marking connection %s as dead", h->module_name, h->name);
1344 
1345  /*
1346  * Don't use this connection, and re-queue all of its
1347  * requests onto other connections.
1348  */
1351 
1352  /*
1353  * We do have status checks. Try to reconnect the
1354  * connection immediately. If the status checks pass,
1355  * then the connection will be marked "alive"
1356  */
1357  if (h->inst->parent->status_check) {
1359  return;
1360  }
1361 
1362  /*
1363  * Revive the connection after a time.
1364  */
1365  if (fr_event_timer_at(h, el, &h->zombie_ev,
1366  fr_time_add(now, h->inst->parent->revive_interval), revive_timeout, tconn) < 0) {
1367  ERROR("Failed inserting revive timeout for connection");
1369  }
1370 }
1371 
1372 
1373 /** See if the connection is zombied.
1374  *
1375  * We check for zombie when major events happen:
1376  *
1377  * 1) request hits its final timeout
1378  * 2) request timer hits, and it needs to be retransmitted
1379  * 3) a DUP packet comes in, and the request needs to be retransmitted
1380  * 4) we're sending a packet.
1381  *
1382  * There MIGHT not be retries configured, so we MUST check for zombie
1383  * when any new packet comes in. Similarly, there MIGHT not be new
1384  * packets, but retries are configured, so we have to check there,
1385  * too.
1386  *
1387  * Also, the socket might not be writable for a while. There MIGHT
1388  * be a long time between getting the timer / DUP signal, and the
1389  * request finally being written to the socket. So we need to check
1390  * for zombie at BOTH the timeout and the mux / write function.
1391  *
1392  * @return
1393  * - true if the connection is zombie.
1394  * - false if the connection is not zombie.
1395  */
1397 {
1398  udp_handle_t *h = talloc_get_type_abort(tconn->conn->h, udp_handle_t);
1399 
1400  /*
1401  * We're replicating, and don't care about the health of
1402  * the home server, and this function should not be called.
1403  */
1404  fr_assert(!h->inst->replicate);
1405 
1406  /*
1407  * If we're status checking OR already zombie, don't go to zombie
1408  */
1409  if (h->status_checking || h->zombie_ev) return true;
1410 
1411  if (fr_time_eq(now, fr_time_wrap(0))) now = fr_time();
1412 
1413  /*
1414  * We received a reply since this packet was sent, the connection isn't zombie.
1415  */
1416  if (fr_time_gteq(h->last_reply, last_sent)) return false;
1417 
1418  /*
1419  * If we've seen ANY response in the allowed window, then the connection is still alive.
1420  */
1421  if (h->inst->parent->synchronous && fr_time_gt(last_sent, fr_time_wrap(0)) &&
1422  (fr_time_lt(fr_time_add(last_sent, h->inst->parent->response_window), now))) return false;
1423 
1424  WARN("%s - Entering Zombie state - connection %s", h->module_name, h->name);
1425  if (h->inst->parent->status_check) {
1426  h->status_checking = true;
1427 
1428  /*
1429  * Queue up the status check packet. It will be sent
1430  * when the connection is writable.
1431  */
1432  h->status_u->retry.start = fr_time_wrap(0);
1433  h->status_r->treq = NULL;
1434 
1436  h->status_u, h->status_r, true) != TRUNK_ENQUEUE_OK) {
1438  }
1439  } else {
1441  zombie_timeout, tconn) < 0) {
1442  ERROR("Failed inserting zombie timeout for connection");
1444  }
1445  }
1446 
1447  return true;
1448 }
1449 
1450 /** Handle timeouts when a request is being sent synchronously
1451  *
1452  */
1454 {
1455  trunk_request_t *treq = talloc_get_type_abort(uctx, trunk_request_t);
1456  udp_request_t *u = talloc_get_type_abort(treq->preq, udp_request_t);
1457  udp_result_t *r = talloc_get_type_abort(treq->rctx, udp_result_t);
1458  trunk_connection_t *tconn = treq->tconn;
1459 
1460  fr_assert(treq->state == TRUNK_REQUEST_STATE_SENT); /* No other states should be timing out */
1461  fr_assert(treq->preq); /* Must still have a protocol request */
1462  fr_assert(u->rr);
1463  fr_assert(tconn);
1464 
1465  r->rcode = RLM_MODULE_FAIL;
1467 
1468  fr_assert(!u->status_check);
1469 
1470  check_for_zombie(el, tconn, now, u->retry.start);
1471 }
1472 
1473 /** Handle retries when a request is being sent asynchronously
1474  *
1475  */
1476 static void request_retry(fr_event_list_t *el, fr_time_t now, void *uctx)
1477 {
1478  trunk_request_t *treq = talloc_get_type_abort(uctx, trunk_request_t);
1479  udp_request_t *u = talloc_get_type_abort(treq->preq, udp_request_t);
1480  udp_result_t *r = talloc_get_type_abort(treq->rctx, udp_result_t);
1481  request_t *request = treq->request;
1482  trunk_connection_t *tconn = treq->tconn;
1483 
1484  fr_assert(treq->state == TRUNK_REQUEST_STATE_SENT); /* No other states should be timing out */
1485  fr_assert(treq->preq); /* Must still have a protocol request */
1486  fr_assert(u->rr);
1487  fr_assert(tconn);
1488 
1489  fr_assert(!u->status_check);
1490 
1491  switch (fr_retry_next(&u->retry, now)) {
1492  /*
1493  * Queue the request for retransmission.
1494  *
1495  * @todo - set up "next" timer here, instead of in
1496  * request_mux() ? That way we can catch the case of
1497  * packets sitting in the queue for extended periods of
1498  * time, and still run the timers.
1499  */
1500  case FR_RETRY_CONTINUE:
1501  trunk_request_requeue(treq);
1502  return;
1503 
1504  case FR_RETRY_MRD:
1505  REDEBUG("Reached maximum_retransmit_duration (%pVs > %pVs), failing request",
1507  break;
1508 
1509  case FR_RETRY_MRC:
1510  REDEBUG("Reached maximum_retransmit_count (%u > %u), failing request",
1511  u->retry.count, u->retry.config->mrc);
1512  break;
1513  }
1514 
1515  r->rcode = RLM_MODULE_FAIL;
1517 
1518  check_for_zombie(el, tconn, now, u->retry.start);
1519 }
1520 
1522 {
1523  trunk_request_t *treq = talloc_get_type_abort(uctx, trunk_request_t);
1524  udp_handle_t *h;
1525  udp_request_t *u = talloc_get_type_abort(treq->preq, udp_request_t);
1526  udp_result_t *r = talloc_get_type_abort(treq->rctx, udp_result_t);
1527  request_t *request = treq->request;
1528  trunk_connection_t *tconn = treq->tconn;
1529 
1530  fr_assert(treq->state == TRUNK_REQUEST_STATE_SENT); /* No other states should be timing out */
1531  fr_assert(treq->preq); /* Must still have a protocol request */
1532  fr_assert(u->rr);
1533  fr_assert(tconn);
1534 
1535  h = talloc_get_type_abort(treq->tconn->conn->h, udp_handle_t);
1536 
1537  fr_assert(u->status_check);
1538 
1539  switch (fr_retry_next(&u->retry, now)) {
1540  /*
1541  * Queue the request for retransmission.
1542  *
1543  * @todo - set up "next" timer here, instead of in
1544  * request_mux() ? That way we can catch the case of
1545  * packets sitting in the queue for extended periods of
1546  * time, and still run the timers.
1547  */
1548  case FR_RETRY_CONTINUE:
1549  trunk_request_requeue(treq);
1550  return;
1551 
1552  case FR_RETRY_MRD:
1553  REDEBUG("Reached maximum_retransmit_duration (%pVs > %pVs), failing request",
1555  break;
1556 
1557  case FR_RETRY_MRC:
1558  REDEBUG("Reached maximum_retransmit_count (%u > %u), failing request",
1559  u->retry.count, u->retry.config->mrc);
1560  break;
1561  }
1562 
1563  r->rcode = RLM_MODULE_FAIL;
1565 
1566  WARN("%s - No response to status check, marking connection as dead - %s", h->module_name, h->name);
1567 
1568  /*
1569  * We're no longer status checking, reconnect the
1570  * connection.
1571  */
1572  h->status_checking = false;
1574 }
1575 
1576 CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function*/
1577 static void request_mux(fr_event_list_t *el,
1578  trunk_connection_t *tconn, connection_t *conn, UNUSED void *uctx)
1579 {
1580  udp_handle_t *h = talloc_get_type_abort(conn->h, udp_handle_t);
1581  rlm_radius_udp_t const *inst = h->inst;
1582  int sent;
1583  uint16_t i, queued;
1584  size_t total_len = 0;
1585 
1586  /*
1587  * Encode multiple packets in preparation
1588  * for transmission with sendmmsg.
1589  */
1590  for (i = 0, queued = 0; (i < inst->max_send_coalesce) && (total_len < h->send_buff_actual); i++) {
1591  trunk_request_t *treq;
1592  udp_request_t *u;
1593  request_t *request;
1594 
1595  if (unlikely(trunk_connection_pop_request(&treq, tconn) < 0)) return;
1596 
1597  /*
1598  * No more requests to send
1599  */
1600  if (!treq) break;
1601 
1602  fr_assert((treq->state == TRUNK_REQUEST_STATE_PENDING) ||
1603  (treq->state == TRUNK_REQUEST_STATE_PARTIAL));
1604 
1605  request = treq->request;
1606  u = talloc_get_type_abort(treq->preq, udp_request_t);
1607 
1608  /*
1609  * Start retransmissions from when the socket is writable.
1610  */
1611  if (fr_time_eq(u->retry.start, fr_time_wrap(0))) {
1612  fr_retry_init(&u->retry, fr_time(), &h->inst->parent->retry[u->code]);
1615  }
1616 
1617  /*
1618  * No previous packet, OR can't retransmit the
1619  * existing one. Oh well.
1620  *
1621  * Note that if we can't retransmit the previous
1622  * packet, then u->rr MUST already have been
1623  * deleted in the request_cancel() function
1624  * or request_release_conn() function when
1625  * the REQUEUE signal was received.
1626  */
1627  if (!u->packet) {
1628  fr_assert(!u->rr);
1629 
1630  if (unlikely(radius_track_entry_reserve(&u->rr, treq, h->tt, request, u->code, treq) < 0)) {
1631 #ifndef NDEBUG
1632  radius_track_state_log(&default_log, L_ERR, __FILE__, __LINE__,
1634 #endif
1635  fr_assert_fail("Tracking entry allocation failed: %s", fr_strerror());
1637  continue;
1638  }
1639  u->id = u->rr->id;
1640 
1641  RDEBUG("Sending %s ID %d length %ld over connection %s",
1642  fr_radius_packet_name[u->code], u->id, u->packet_len, h->name);
1643 
1644  if (encode(h->inst, request, u, u->id) < 0) {
1645  /*
1646  * Need to do this because request_conn_release
1647  * may not be called.
1648  */
1649  udp_request_reset(u);
1650  if (u->ev) (void) fr_event_timer_delete(&u->ev);
1652  continue;
1653  }
1654  RHEXDUMP3(u->packet, u->packet_len, "Encoded packet");
1655 
1656  /*
1657  * Remember the authentication vector, which now has the
1658  * packet signature.
1659  */
1661  } else {
1662  RDEBUG("Retransmitting %s ID %d length %ld over connection %s",
1663  fr_radius_packet_name[u->code], u->id, u->packet_len, h->name);
1664  }
1665 
1666  log_request_pair_list(L_DBG_LVL_2, request, NULL, &request->request_pairs, NULL);
1667  if (!fr_pair_list_empty(&u->extra)) log_request_pair_list(L_DBG_LVL_2, request, NULL, &u->extra, NULL);
1668 
1669  /*
1670  * Record pointers to the buffer we'll be writing
1671  * We store the treq so we can place it back in
1672  * the pending state if the sendmmsg call fails.
1673  */
1674  h->coalesced[queued].treq = treq;
1675  h->coalesced[queued].out.iov_base = u->packet;
1676  h->coalesced[queued].out.iov_len = u->packet_len;
1677 
1678  /*
1679  * Record how much data we have in total.
1680  *
1681  * Try not to exceed the SO_SNDBUF value of the
1682  * socket as we potentially just waste CPU
1683  * time re-encoding the packets.
1684  */
1685  total_len += u->packet_len;
1686 
1687  /*
1688  * Tell the trunk API that this request is now in
1689  * the "sent" state. And we don't want to see
1690  * this request again. The request hasn't actually
1691  * been sent, but it's the only way to get at the
1692  * next entry in the heap.
1693  */
1695  queued++;
1696  }
1697  if (queued == 0) return; /* No work */
1698 
1699  /*
1700  * Verify nothing accidentally freed the connection handle
1701  */
1702  (void)talloc_get_type_abort(h, udp_handle_t);
1703 
1704  /*
1705  * Send the coalesced datagrams
1706  */
1707  sent = sendmmsg(h->fd, h->mmsgvec, queued, 0);
1708  if (sent < 0) { /* Error means no messages were sent */
1709  sent = 0;
1710 
1711  /*
1712  * Temporary conditions
1713  */
1714  switch (errno) {
1715 #if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN)
1716  case EWOULDBLOCK: /* No outbound packet buffers, maybe? */
1717 #endif
1718  case EAGAIN: /* No outbound packet buffers, maybe? */
1719  case EINTR: /* Interrupted by signal */
1720  case ENOBUFS: /* No outbound packet buffers, maybe? */
1721  case ENOMEM: /* malloc failure in kernel? */
1722  WARN("%s - Failed sending data over connection %s: %s",
1723  h->module_name, h->name, fr_syserror(errno));
1724  break;
1725 
1726  /*
1727  * Fatal, request specific conditions
1728  *
1729  * sendmmsg will only return an error condition if the
1730  * first packet being sent errors.
1731  *
1732  * When we get request specific errors, we need to fail
1733  * the first request in the set, and move the rest of
1734  * the packets back to the pending state.
1735  */
1736  case EMSGSIZE: /* Packet size exceeds max size allowed on socket */
1737  ERROR("%s - Failed sending data over connection %s: %s",
1738  h->module_name, h->name, fr_syserror(errno));
1740  sent = 1;
1741  break;
1742 
1743  /*
1744  * Will re-queue any 'sent' requests, so we don't
1745  * have to do any cleanup.
1746  */
1747  default:
1748  ERROR("%s - Failed sending data over connection %s: %s",
1749  h->module_name, h->name, fr_syserror(errno));
1751  return;
1752  }
1753  }
1754 
1755  /*
1756  * For all messages that were actually sent by sendmmsg
1757  * start the request timer.
1758  */
1759  for (i = 0; i < sent; i++) {
1760  trunk_request_t *treq = h->coalesced[i].treq;
1761  udp_request_t *u;
1762  request_t *request;
1763  char const *action;
1764 
1765  /*
1766  * It's UDP so there should never be partial writes
1767  */
1768  fr_assert((size_t)h->mmsgvec[i].msg_len == h->mmsgvec[i].msg_hdr.msg_iov->iov_len);
1769 
1770  fr_assert(treq->state == TRUNK_REQUEST_STATE_SENT);
1771 
1772  request = treq->request;
1773  u = talloc_get_type_abort(treq->preq, udp_request_t);
1774 
1775  /*
1776  * Tell the admin what's going on
1777  */
1778  if (u->retry.count == 1) {
1779  action = inst->parent->originate ? "Originated" : "Proxied";
1780  h->last_sent = u->retry.start;
1781  if (fr_time_lteq(h->first_sent, h->last_idle)) h->first_sent = h->last_sent;
1782 
1783  } else {
1784  action = "Retransmitted";
1785  }
1786 
1787  if (u->status_check) {
1788  RDEBUG("%s status check. Expecting response within %pVs", action,
1789  fr_box_time_delta(u->retry.rt));
1790 
1791  if (fr_event_timer_at(u, el, &u->ev, u->retry.next, status_check_retry, treq) < 0) {
1792  RERROR("Failed inserting retransmit timeout for connection");
1794  continue;
1795  }
1796 
1797  } else if (!inst->parent->synchronous) {
1798  RDEBUG("%s request. Expecting response within %pVs", action,
1799  fr_box_time_delta(u->retry.rt));
1800 
1801  if (fr_event_timer_at(u, el, &u->ev, u->retry.next, request_retry, treq) < 0) {
1802  RERROR("Failed inserting retransmit timeout for connection");
1804  continue;
1805  }
1806 
1807  } else if (u->retry.count == 1) {
1808  if (fr_event_timer_at(u, el, &u->ev,
1810  request_timeout, treq) < 0) {
1811  RERROR("Failed inserting timeout for connection");
1813  continue;
1814  }
1815 
1816  /*
1817  * If the packet doesn't get a response,
1818  * then udp_request_free() will notice, and run conn_zombie()
1819  */
1820  RDEBUG("%s request. Relying on NAS to perform more retransmissions", action);
1821  }
1822  }
1823 
1824  /*
1825  * Requests that weren't sent get re-enqueued
1826  *
1827  * The cancel logic runs as per-normal and cleans up
1828  * the request ready for sending again...
1829  */
1830  for (i = sent; i < queued; i++) trunk_request_requeue(h->coalesced[i].treq);
1831 }
1832 
1833 CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function*/
1834 static void request_mux_replicate(UNUSED fr_event_list_t *el,
1835  trunk_connection_t *tconn, connection_t *conn, UNUSED void *uctx)
1836 {
1837  udp_handle_t *h = talloc_get_type_abort(conn->h, udp_handle_t);
1838  rlm_radius_udp_t const *inst = h->inst;
1839 
1840  uint16_t i = 0, queued;
1841  int sent;
1842  size_t total_len = 0;
1843 
1844  for (i = 0, queued = 0; (i < inst->max_send_coalesce) && (total_len < h->send_buff_actual); i++) {
1845  trunk_request_t *treq;
1846  udp_request_t *u;
1847  request_t *request;
1848 
1849  if (unlikely(trunk_connection_pop_request(&treq, tconn) < 0)) return;
1850 
1851  /*
1852  * No more requests to send
1853  */
1854  if (!treq) break;
1855 
1856  fr_assert((treq->state == TRUNK_REQUEST_STATE_PENDING) ||
1857  (treq->state == TRUNK_REQUEST_STATE_PARTIAL));
1858 
1859  request = treq->request;
1860  u = talloc_get_type_abort(treq->preq, udp_request_t);
1861 
1862  if (!u->packet) {
1863  u->id = h->last_id++;
1864 
1865  if (encode(h->inst, request, u, u->id) < 0) {
1867  continue;
1868  }
1869  }
1870 
1871  RDEBUG("Sending %s ID %d length %ld over connection %s",
1872  fr_radius_packet_name[u->code], u->id, u->packet_len, h->name);
1873  RHEXDUMP3(u->packet, u->packet_len, "Encoded packet");
1874 
1875  h->coalesced[queued].treq = treq;
1876  h->coalesced[queued].out.iov_base = u->packet;
1877  h->coalesced[queued].out.iov_len = u->packet_len;
1878 
1879  /*
1880  * Record how much data we have in total.
1881  *
1882  * Try not to exceed the SO_SNDBUF value of the
1883  * socket as we potentially just waste CPU
1884  * time re-encoding the packets.
1885  */
1886  total_len += u->packet_len;
1887 
1889  queued++;
1890  }
1891  if (queued == 0) return; /* No work */
1892 
1893  /*
1894  * Verify nothing accidentally freed the connection handle
1895  */
1896  (void)talloc_get_type_abort(h, udp_handle_t);
1897 
1898  sent = sendmmsg(h->fd, h->mmsgvec, queued, 0);
1899  if (sent < 0) { /* Error means no messages were sent */
1900  sent = 0;
1901 
1902  /*
1903  * Temporary conditions
1904  */
1905  switch (errno) {
1906 #if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN)
1907  case EWOULDBLOCK: /* No outbound packet buffers, maybe? */
1908 #endif
1909  case EAGAIN: /* No outbound packet buffers, maybe? */
1910  case EINTR: /* Interrupted by signal */
1911  case ENOBUFS: /* No outbound packet buffers, maybe? */
1912  case ENOMEM: /* malloc failure in kernel? */
1913  WARN("%s - Failed sending data over connection %s: %s",
1914  h->module_name, h->name, fr_syserror(errno));
1915  break;
1916 
1917  /*
1918  * Fatal, request specific conditions
1919  *
1920  * sendmmsg will only return an error condition if the
1921  * first packet being sent errors.
1922  *
1923  * When we get request specific errors, we need to fail
1924  * the first request in the set, and move the rest of
1925  * the packets back to the pending state.
1926  */
1927  case EMSGSIZE: /* Packet size exceeds max size allowed on socket */
1928  ERROR("%s - Failed sending data over connection %s: %s",
1929  h->module_name, h->name, fr_syserror(errno));
1931  sent = 1;
1932  break;
1933 
1934  /*
1935  * Will re-queue any 'sent' requests, so we don't
1936  * have to do any cleanup.
1937  */
1938  default:
1939  ERROR("%s - Failed sending data over connection %s: %s",
1940  h->module_name, h->name, fr_syserror(errno));
1942  return;
1943  }
1944  }
1945 
1946  for (i = 0; i < sent; i++) {
1947  trunk_request_t *treq = h->coalesced[i].treq;
1948  udp_result_t *r = talloc_get_type_abort(treq->rctx, udp_result_t);
1949 
1950  /*
1951  * It's UDP so there should never be partial writes
1952  */
1953  fr_assert((size_t)h->mmsgvec[i].msg_len == h->mmsgvec[i].msg_hdr.msg_iov->iov_len);
1954 
1955  r->rcode = RLM_MODULE_OK;
1957  }
1958 
1959  for (i = sent; i < queued; i++) trunk_request_requeue(h->coalesced[i].treq);
1960 }
1961 
1962 /** Deal with Protocol-Error replies, and possible negotiation
1963  *
1964  */
1966 {
1967  bool error_601 = false;
1968  uint32_t response_length = 0;
1969  uint8_t const *attr, *end;
1970 
1971  end = h->buffer + fr_nbo_to_uint16(h->buffer + 2);
1972 
1973  for (attr = h->buffer + RADIUS_HEADER_LENGTH;
1974  attr < end;
1975  attr += attr[1]) {
1976  /*
1977  * Error-Cause = Response-Too-Big
1978  */
1979  if ((attr[0] == attr_error_cause->attr) && (attr[1] == 6)) {
1980  uint32_t error;
1981 
1982  memcpy(&error, attr + 2, 4);
1983  error = ntohl(error);
1984  if (error == 601) error_601 = true;
1985  continue;
1986  }
1987 
1988  /*
1989  * The other end wants us to increase our Response-Length
1990  */
1991  if ((attr[0] == attr_response_length->attr) && (attr[1] == 6)) {
1992  memcpy(&response_length, attr + 2, 4);
1993  continue;
1994  }
1995 
1996  /*
1997  * Protocol-Error packets MUST contain an
1998  * Original-Packet-Code attribute.
1999  *
2000  * The attribute containing the
2001  * Original-Packet-Code is an extended
2002  * attribute.
2003  */
2004  if (attr[0] != attr_extended_attribute_1->attr) continue;
2005 
2006  /*
2007  * ATTR + LEN + EXT-Attr + uint32
2008  */
2009  if (attr[1] != 7) continue;
2010 
2011  /*
2012  * See if there's an Original-Packet-Code.
2013  */
2014  if (attr[2] != (uint8_t)attr_original_packet_code->attr) continue;
2015 
2016  /*
2017  * Has to be an 8-bit number.
2018  */
2019  if ((attr[3] != 0) ||
2020  (attr[4] != 0) ||
2021  (attr[5] != 0)) {
2022  if (r) r->rcode = RLM_MODULE_FAIL;
2023  return;
2024  }
2025 
2026  /*
2027  * The value has to match. We don't
2028  * currently multiplex different codes
2029  * with the same IDs on connections. So
2030  * this check is just for RFC compliance,
2031  * and for sanity.
2032  */
2033  if (attr[6] != u->code) {
2034  if (r) r->rcode = RLM_MODULE_FAIL;
2035  return;
2036  }
2037  }
2038 
2039  /*
2040  * Error-Cause = Response-Too-Big
2041  *
2042  * The other end says it needs more room to send it's response
2043  *
2044  * Limit it to reasonable values.
2045  */
2046  if (error_601 && response_length && (response_length > h->buflen)) {
2047  if (response_length < 4096) response_length = 4096;
2048  if (response_length > 65535) response_length = 65535;
2049 
2050  DEBUG("%s - Increasing buffer size to %u for connection %s", h->module_name, response_length, h->name);
2051 
2052  /*
2053  * Make sure to copy the packet over!
2054  */
2055  attr = h->buffer;
2056  h->buflen = response_length;
2057  MEM(h->buffer = talloc_array(h, uint8_t, h->buflen));
2058 
2059  memcpy(h->buffer, attr, end - attr);
2060  }
2061 
2062  /*
2063  * fail - something went wrong internally, or with the connection.
2064  * invalid - wrong response to packet
2065  * handled - best remaining alternative :(
2066  *
2067  * i.e. if the response is NOT accept, reject, whatever,
2068  * then we shouldn't allow the caller to do any more
2069  * processing of this packet. There was a protocol
2070  * error, and the response is valid, but not useful for
2071  * anything.
2072  */
2073  if (r) r->rcode = RLM_MODULE_HANDLED;
2074 }
2075 
2076 
2077 /** Handle retries for a status check
2078  *
2079  */
2081 {
2082  trunk_connection_t *tconn = talloc_get_type_abort(uctx, trunk_connection_t);
2083  udp_handle_t *h = talloc_get_type_abort(tconn->conn->h, udp_handle_t);
2084 
2086  h->status_u, h->status_r, true) != TRUNK_ENQUEUE_OK) {
2088  }
2089 }
2090 
2091 
2092 /** Deal with replies replies to status checks and possible negotiation
2093  *
2094  */
2096 {
2097  udp_handle_t *h = talloc_get_type_abort(treq->tconn->conn->h, udp_handle_t);
2098  rlm_radius_t const *inst = h->inst->parent;
2099  udp_request_t *u = talloc_get_type_abort(treq->preq, udp_request_t);
2100  udp_result_t *r = talloc_get_type_abort(treq->rctx, udp_result_t);
2101 
2102  fr_assert(treq->preq == h->status_u);
2103  fr_assert(treq->rctx == h->status_r);
2104 
2105  r->treq = NULL;
2106 
2107  /*
2108  * @todo - do other negotiation and signaling.
2109  */
2110  if (h->buffer[0] == FR_RADIUS_CODE_PROTOCOL_ERROR) protocol_error_reply(u, NULL, h);
2111 
2112  if (u->num_replies < inst->num_answers_to_alive) {
2113  DEBUG("Received %d / %u replies for status check, on connection - %s",
2114  u->num_replies, inst->num_answers_to_alive, h->name);
2115  DEBUG("Next status check packet will be in %pVs", fr_box_time_delta(fr_time_sub(u->retry.next, now)));
2116 
2117  /*
2118  * Set the timer for the next retransmit.
2119  */
2120  if (fr_event_timer_at(h, h->thread->el, &u->ev, u->retry.next, status_check_next, treq->tconn) < 0) {
2122  }
2123  return;
2124  }
2125 
2126  DEBUG("Received enough replies to status check, marking connection as active - %s", h->name);
2127 
2128  /*
2129  * Set the "last idle" time to now, so that we don't
2130  * restart zombie_period until sufficient time has
2131  * passed.
2132  */
2133  h->last_idle = fr_time();
2134 
2135  /*
2136  * Reset retry interval and retransmission counters
2137  * also frees u->ev.
2138  */
2139  status_check_reset(h, u);
2140  trunk_connection_signal_active(treq->tconn);
2141 }
2142 
2143 CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function*/
2145 {
2146  udp_handle_t *h = talloc_get_type_abort(conn->h, udp_handle_t);
2147 
2148  DEBUG3("%s - Reading data for connection %s", h->module_name, h->name);
2149 
2150  while (true) {
2151  ssize_t slen;
2152 
2153  trunk_request_t *treq;
2154  request_t *request;
2155  udp_request_t *u;
2156  udp_result_t *r;
2158  decode_fail_t reason;
2159  uint8_t code = 0;
2160  fr_pair_list_t reply;
2161 
2162  fr_time_t now;
2163 
2164  fr_pair_list_init(&reply);
2165  /*
2166  * Drain the socket of all packets. If we're busy, this
2167  * saves a round through the event loop. If we're not
2168  * busy, a few extra system calls don't matter.
2169  */
2170  slen = read(h->fd, h->buffer, h->buflen);
2171  if (slen == 0) return;
2172 
2173  if (slen < 0) {
2174  if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) return;
2175 
2176  ERROR("%s - Failed reading response from socket: %s",
2177  h->module_name, fr_syserror(errno));
2179  return;
2180  }
2181 
2182  if (slen < RADIUS_HEADER_LENGTH) {
2183  ERROR("%s - Packet too short, expected at least %zu bytes got %zd bytes",
2184  h->module_name, (size_t)RADIUS_HEADER_LENGTH, slen);
2185  continue;
2186  }
2187 
2188  /*
2189  * Note that we don't care about packet codes. All
2190  * packet codes share the same ID space.
2191  */
2192  rr = radius_track_entry_find(h->tt, h->buffer[1], NULL);
2193  if (!rr) {
2194  WARN("%s - Ignoring reply with ID %i that arrived too late",
2195  h->module_name, h->buffer[1]);
2196  continue;
2197  }
2198 
2199  treq = talloc_get_type_abort(rr->uctx, trunk_request_t);
2200  request = treq->request;
2201  fr_assert(request != NULL);
2202  u = talloc_get_type_abort(treq->preq, udp_request_t);
2203  r = talloc_get_type_abort(treq->rctx, udp_result_t);
2204 
2205  /*
2206  * Validate and decode the incoming packet
2207  */
2208 
2209  if (!check(h, &slen)) {
2210  RWARN("Ignoring malformed packet");
2211  continue;
2212  }
2213 
2214  reason = decode(request->reply_ctx, &reply, &code, h, request, u, rr->vector, h->buffer, (size_t)slen);
2215  if (reason != DECODE_FAIL_NONE) continue;
2216 
2217  /*
2218  * Only valid packets are processed
2219  * Otherwise an attacker could perform
2220  * a DoS attack against the proxying servers
2221  * by sending fake responses for upstream
2222  * servers.
2223  */
2224  h->last_reply = now = fr_time();
2225 
2226  /*
2227  * Status-Server can have any reply code, we don't care
2228  * what it is. So long as it's signed properly, we
2229  * accept it. This flexibility is because we don't
2230  * expose Status-Server to the admins. It's only used by
2231  * this module for internal signalling.
2232  */
2233  if (u == h->status_u) {
2234  fr_pair_list_free(&reply); /* Probably want to pass this to status_check_reply? */
2235  status_check_reply(treq, now);
2237  continue;
2238  }
2239 
2240  /*
2241  * Handle any state changes, etc. needed by receiving a
2242  * Protocol-Error reply packet.
2243  *
2244  * Protocol-Error is permitted as a reply to any
2245  * packet.
2246  */
2247  switch (code) {
2249  protocol_error_reply(u, r, h);
2250  break;
2251 
2252  default:
2253  break;
2254  }
2255 
2256  /*
2257  * Mark up the request as being an Access-Challenge, if
2258  * required.
2259  *
2260  * We don't do this for other packet types, because the
2261  * ok/fail nature of the module return code will
2262  * automatically result in it the parent request
2263  * returning an ok/fail packet code.
2264  */
2266  fr_pair_t *vp;
2267 
2268  vp = fr_pair_find_by_da(&request->reply_pairs, NULL, attr_packet_type);
2269  if (!vp) {
2270  MEM(vp = fr_pair_afrom_da(request->reply_ctx, attr_packet_type));
2271  vp->vp_uint32 = FR_RADIUS_CODE_ACCESS_CHALLENGE;
2272  fr_pair_append(&request->reply_pairs, vp);
2273  }
2274  }
2275 
2276  /*
2277  * Delete Proxy-State attributes from the reply.
2278  */
2280 
2281  /*
2282  * If the reply has Message-Authenticator, delete
2283  * it from the proxy reply so that it isn't
2284  * copied over to our reply. But also create a
2285  * reply.Message-Authenticator attribute, so that
2286  * it ends up in our reply.
2287  */
2288  if (fr_pair_find_by_da(&reply, NULL, attr_message_authenticator)) {
2289  fr_pair_t *vp;
2290 
2292 
2293  MEM(vp = fr_pair_afrom_da(request->reply_ctx, attr_message_authenticator));
2294  (void) fr_pair_value_memdup(vp, (uint8_t const *) "", 1, false);
2295  fr_pair_append(&request->reply_pairs, vp);
2296  }
2297 
2298  treq->request->reply->code = code;
2299  r->rcode = radius_code_to_rcode[code];
2300  fr_pair_list_append(&request->reply_pairs, &reply);
2302  }
2303 }
2304 
2305 /** Remove the request from any tracking structures
2306  *
2307  * Frees encoded packets if the request is being moved to a new connection
2308  */
2309 static void request_cancel(UNUSED connection_t *conn, void *preq_to_reset,
2310  trunk_cancel_reason_t reason, UNUSED void *uctx)
2311 {
2312  udp_request_t *u = talloc_get_type_abort(preq_to_reset, udp_request_t);
2313 
2314  /*
2315  * Request has been requeued on the same
2316  * connection due to timeout or DUP signal. We
2317  * keep the same packet to avoid re-encoding it.
2318  */
2319  if (reason == TRUNK_CANCEL_REASON_REQUEUE) {
2320  /*
2321  * Delete the request_timeout
2322  *
2323  * Note: There might not be a request timeout
2324  * set in the case where the request was
2325  * queued for sendmmsg but never actually
2326  * sent.
2327  */
2328  if (u->ev) (void) fr_event_timer_delete(&u->ev);
2329  }
2330 
2331  /*
2332  * Other cancellations are dealt with by
2333  * request_conn_release as the request is removed
2334  * from the trunk.
2335  */
2336 }
2337 
2338 /** Clear out anything associated with the handle from the request
2339  *
2340  */
2341 static void request_conn_release(connection_t *conn, void *preq_to_reset, UNUSED void *uctx)
2342 {
2343  udp_request_t *u = talloc_get_type_abort(preq_to_reset, udp_request_t);
2344  udp_handle_t *h = talloc_get_type_abort(conn->h, udp_handle_t);
2345 
2346  if (u->ev) (void)fr_event_timer_delete(&u->ev);
2347  if (u->packet) udp_request_reset(u);
2348 
2349  u->num_replies = 0;
2350 
2351  /*
2352  * If there are no outstanding tracking entries
2353  * allocated then the connection is "idle".
2354  */
2355  if (!h->tt || (h->tt->num_requests == 0)) h->last_idle = fr_time();
2356 }
2357 
2358 /** Clear out anything associated with the handle from the request
2359  *
2360  */
2361 static void request_conn_release_replicate(UNUSED connection_t *conn, void *preq_to_reset, UNUSED void *uctx)
2362 {
2363  udp_request_t *u = talloc_get_type_abort(preq_to_reset, udp_request_t);
2364 
2365  fr_assert(!u->ev);
2366 
2367  if (u->packet) udp_request_reset(u);
2368 }
2369 
2370 /** Write out a canned failure
2371  *
2372  */
2373 static void request_fail(request_t *request, void *preq, void *rctx,
2375 {
2376  udp_result_t *r = talloc_get_type_abort(rctx, udp_result_t);
2377  udp_request_t *u = talloc_get_type_abort(preq, udp_request_t);
2378 
2379  fr_assert(!u->rr && !u->packet && fr_pair_list_empty(&u->extra) && !u->ev); /* Dealt with by request_conn_release */
2380 
2382 
2383  if (u->status_check) return;
2384 
2385  r->rcode = RLM_MODULE_FAIL;
2386  r->treq = NULL;
2387 
2389 }
2390 
2391 /** Response has already been written to the rctx at this point
2392  *
2393  */
2394 static void request_complete(request_t *request, void *preq, void *rctx, UNUSED void *uctx)
2395 {
2396  udp_result_t *r = talloc_get_type_abort(rctx, udp_result_t);
2397  udp_request_t *u = talloc_get_type_abort(preq, udp_request_t);
2398 
2399  fr_assert(!u->rr && !u->packet && fr_pair_list_empty(&u->extra) && !u->ev); /* Dealt with by request_conn_release */
2400 
2401  if (u->status_check) return;
2402 
2403  r->treq = NULL;
2404 
2406 }
2407 
2408 /** Explicitly free resources associated with the protocol request
2409  *
2410  */
2411 static void request_free(UNUSED request_t *request, void *preq_to_free, UNUSED void *uctx)
2412 {
2413  udp_request_t *u = talloc_get_type_abort(preq_to_free, udp_request_t);
2414 
2415  fr_assert(!u->rr && !u->packet && fr_pair_list_empty(&u->extra) && !u->ev); /* Dealt with by request_conn_release */
2416 
2417  /*
2418  * Don't free status check requests.
2419  */
2420  if (u->status_check) return;
2421 
2422  talloc_free(u);
2423 }
2424 
2425 /** Resume execution of the request, returning the rcode set during trunk execution
2426  *
2427  */
2428 static unlang_action_t mod_resume(rlm_rcode_t *p_result, module_ctx_t const *mctx, UNUSED request_t *request)
2429 {
2430  udp_result_t *r = talloc_get_type_abort(mctx->rctx, udp_result_t);
2431  rlm_rcode_t rcode = r->rcode;
2432 
2433  talloc_free(r);
2434 
2435  RETURN_MODULE_RCODE(rcode);
2436 }
2437 
2438 static void mod_signal(module_ctx_t const *mctx, UNUSED request_t *request, fr_signal_t action)
2439 {
2440  udp_thread_t *t = talloc_get_type_abort(mctx->thread, udp_thread_t);
2441  udp_result_t *r = talloc_get_type_abort(mctx->rctx, udp_result_t);
2442 
2443  /*
2444  * If we don't have a treq associated with the
2445  * rctx it's likely because the request was
2446  * scheduled, but hasn't yet been resumed, and
2447  * has received a signal, OR has been resumed
2448  * and immediately cancelled as the event loop
2449  * is exiting, in which case
2450  * unlang_request_is_scheduled will return false
2451  * (don't use it).
2452  */
2453  if (!r->treq) {
2454  talloc_free(r);
2455  return;
2456  }
2457 
2458  switch (action) {
2459  /*
2460  * The request is being cancelled, tell the
2461  * trunk so it can clean up the treq.
2462  */
2463  case FR_SIGNAL_CANCEL:
2465  r->treq = NULL;
2466  talloc_free(r); /* Should be freed soon anyway, but better to be explicit */
2467  return;
2468 
2469  /*
2470  * Requeue the request on the same connection
2471  * causing a "retransmission" if the request
2472  * has already been sent out.
2473  */
2474  case FR_SIGNAL_DUP:
2475  /*
2476  * If we're not synchronous, then rely on
2477  * request_retry() to do the retransmissions.
2478  */
2479  if (!t->inst->parent->synchronous) return;
2480 
2481  /*
2482  * We are synchronous, retransmit the current
2483  * request on the same connection.
2484  *
2485  * If it's zombie, we still resend it. If the
2486  * connection is dead, then a callback will move
2487  * this request to a new connection.
2488  */
2490  return;
2491 
2492  default:
2493  return;
2494  }
2495 }
2496 
2497 #ifndef NDEBUG
2498 /** Free a udp_result_t
2499  *
2500  * Allows us to set break points for debugging.
2501  */
2503 {
2504  trunk_request_t *treq;
2505  udp_request_t *u;
2506 
2507  if (!r->treq) return 0;
2508 
2509  treq = talloc_get_type_abort(r->treq, trunk_request_t);
2510  u = talloc_get_type_abort(treq->preq, udp_request_t);
2511 
2512  fr_assert_msg(!u->ev, "udp_result_t freed with active timer");
2513 
2514  return 0;
2515 }
2516 #endif
2517 
2518 /** Free a udp_request_t
2519  */
2521 {
2522  if (u->ev) (void) fr_event_timer_delete(&u->ev);
2523 
2524  fr_assert(u->rr == NULL);
2525 
2526  return 0;
2527 }
2528 
2529 static unlang_action_t mod_enqueue(rlm_rcode_t *p_result, void **rctx_out, void *instance, void *thread, request_t *request)
2530 {
2531  rlm_radius_udp_t *inst = talloc_get_type_abort(instance, rlm_radius_udp_t);
2532  udp_thread_t *t = talloc_get_type_abort(thread, udp_thread_t);
2533  udp_result_t *r;
2534  udp_request_t *u;
2535  trunk_request_t *treq;
2536 
2537  fr_assert(request->packet->code > 0);
2538  fr_assert(request->packet->code < FR_RADIUS_CODE_MAX);
2539 
2540  if (request->packet->code == FR_RADIUS_CODE_STATUS_SERVER) {
2541  RWDEBUG("Status-Server is reserved for internal use, and cannot be sent manually.");
2543  }
2544 
2545  treq = trunk_request_alloc(t->trunk, request);
2546  if (!treq) RETURN_MODULE_FAIL;
2547 
2548  MEM(r = talloc_zero(request, udp_result_t));
2549 #ifndef NDEBUG
2550  talloc_set_destructor(r, _udp_result_free);
2551 #endif
2552 
2553  /*
2554  * Can't use compound literal - const issues.
2555  */
2556  MEM(u = talloc_zero(treq, udp_request_t));
2557  u->code = request->packet->code;
2558  u->synchronous = inst->parent->synchronous;
2559  u->priority = request->async->priority;
2560  u->recv_time = request->async->recv_time;
2561  fr_pair_list_init(&u->extra);
2562 
2563  r->rcode = RLM_MODULE_FAIL;
2564 
2565  /*
2566  * Make sure that we print out the actual encoded value
2567  * of the Message-Authenticator attribute. If the caller
2568  * asked for one, delete theirs (which has a bad value),
2569  * and remember to add one manually when we encode the
2570  * packet. This is the only editing we do on the input
2571  * request.
2572  *
2573  * @todo - don't edit the input packet!
2574  */
2575  if (fr_pair_find_by_da(&request->request_pairs, NULL, attr_message_authenticator)) {
2578  }
2579 
2580  switch(trunk_request_enqueue(&treq, t->trunk, request, u, r)) {
2581  case TRUNK_ENQUEUE_OK:
2583  break;
2584 
2586  REDEBUG("Unable to queue packet - connections at maximum capacity");
2587  fail:
2588  fr_assert(!u->rr && !u->packet); /* Should not have been fed to the muxer */
2589  trunk_request_free(&treq); /* Return to the free list */
2590  talloc_free(r);
2592 
2594  REDEBUG("All destinations are down - cannot send packet");
2595  goto fail;
2596 
2597  case TRUNK_ENQUEUE_FAIL:
2598  REDEBUG("Unable to queue packet");
2599  goto fail;
2600  }
2601 
2602  r->treq = treq; /* Remember for signalling purposes */
2603 
2604  talloc_set_destructor(u, _udp_request_free);
2605 
2606  *rctx_out = r;
2607 
2608  return UNLANG_ACTION_YIELD;
2609 }
2610 
2611 /** Instantiate thread data for the submodule.
2612  *
2613  */
2615 {
2616  rlm_radius_udp_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_radius_udp_t);
2617  udp_thread_t *thread = talloc_get_type_abort(mctx->thread, udp_thread_t);
2618 
2619  static trunk_io_funcs_t io_funcs = {
2620  .connection_alloc = thread_conn_alloc,
2621  .connection_notify = thread_conn_notify,
2622  .request_prioritise = request_prioritise,
2623  .request_mux = request_mux,
2624  .request_demux = request_demux,
2625  .request_conn_release = request_conn_release,
2626  .request_complete = request_complete,
2627  .request_fail = request_fail,
2628  .request_cancel = request_cancel,
2629  .request_free = request_free
2630  };
2631 
2632  static trunk_io_funcs_t io_funcs_replicate = {
2633  .connection_alloc = thread_conn_alloc,
2634  .connection_notify = thread_conn_notify_replicate,
2635  .request_prioritise = request_prioritise,
2636  .request_mux = request_mux_replicate,
2637  .request_conn_release = request_conn_release_replicate,
2638  .request_complete = request_complete,
2639  .request_fail = request_fail,
2640  .request_free = request_free
2641  };
2642 
2643  thread->el = mctx->el;
2644  thread->inst = inst;
2645  thread->trunk = trunk_alloc(thread, mctx->el, inst->replicate ? &io_funcs_replicate : &io_funcs,
2646  &inst->trunk_conf, inst->parent->name, thread, false);
2647  if (!thread->trunk) return -1;
2648 
2649  return 0;
2650 }
2651 
2652 static int mod_instantiate(module_inst_ctx_t const *mctx)
2653 {
2654  rlm_radius_t *parent = talloc_get_type_abort(mctx->mi->parent->data, rlm_radius_t);
2655  rlm_radius_udp_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_radius_udp_t);
2656  CONF_SECTION *conf = mctx->mi->conf;
2657 
2658  if (!parent) {
2659  ERROR("IO module cannot be instantiated directly");
2660  return -1;
2661  }
2662 
2663  inst->parent = parent;
2664  inst->replicate = parent->replicate;
2665 
2666  /*
2667  * Always need at least one mmsgvec
2668  */
2669  if (inst->max_send_coalesce == 0) inst->max_send_coalesce = 1;
2670 
2671  /*
2672  * Ensure that we have a destination address.
2673  */
2674  if (inst->dst_ipaddr.af == AF_UNSPEC) {
2675  cf_log_err(conf, "A value must be given for 'ipaddr'");
2676  return -1;
2677  }
2678 
2679  inst->common_ctx = (fr_radius_ctx_t) {
2680  .secret = inst->secret,
2681  .secret_length = talloc_array_length(inst->secret) - 1,
2682  .proxy_state = inst->parent->proxy_state,
2683  };
2684 
2685  /*
2686  * If src_ipaddr isn't set, make sure it's INADDR_ANY, of
2687  * the same address family as dst_ipaddr.
2688  */
2689  if (inst->src_ipaddr.af == AF_UNSPEC) {
2690  memset(&inst->src_ipaddr, 0, sizeof(inst->src_ipaddr));
2691 
2692  inst->src_ipaddr.af = inst->dst_ipaddr.af;
2693 
2694  if (inst->src_ipaddr.af == AF_INET) {
2695  inst->src_ipaddr.prefix = 32;
2696  } else {
2697  inst->src_ipaddr.prefix = 128;
2698  }
2699  }
2700 
2701  else if (inst->src_ipaddr.af != inst->dst_ipaddr.af) {
2702  cf_log_err(conf, "The 'ipaddr' and 'src_ipaddr' configuration items must "
2703  "be both of the same address family");
2704  return -1;
2705  }
2706 
2707  if (!inst->dst_port) {
2708  cf_log_err(conf, "A value must be given for 'port'");
2709  return -1;
2710  }
2711 
2712  /*
2713  * Clamp max_packet_size first before checking recv_buff and send_buff
2714  */
2715  FR_INTEGER_BOUND_CHECK("max_packet_size", inst->max_packet_size, >=, 64);
2716  FR_INTEGER_BOUND_CHECK("max_packet_size", inst->max_packet_size, <=, 65535);
2717 
2718 
2719 #ifdef __linux__
2720  if (inst->replicate) {
2721  /*
2722  * Replicating: Set the receive buffer to zero.
2723  */
2724  inst->recv_buff_is_set = true;
2725 
2726  /*
2727  * On Linux this has the effect of discarding
2728  * all incoming data in the kernel.
2729  * With macOS and others it's an invalid value.
2730  */
2731 
2732  inst->recv_buff = 0;
2733  } else {
2734 #endif
2735  if (inst->recv_buff_is_set) {
2736  FR_INTEGER_BOUND_CHECK("recv_buff", inst->recv_buff, >=, inst->max_packet_size);
2737  FR_INTEGER_BOUND_CHECK("recv_buff", inst->recv_buff, <=, (1 << 30));
2738  }
2739 #ifdef __linux__
2740  }
2741 #endif
2742 
2743  if (inst->send_buff_is_set) {
2744  FR_INTEGER_BOUND_CHECK("send_buff", inst->send_buff, >=, inst->max_packet_size);
2745  FR_INTEGER_BOUND_CHECK("send_buff", inst->send_buff, <=, (1 << 30));
2746  }
2747 
2748  memcpy(&inst->trunk_conf, &inst->parent->trunk_conf, sizeof(inst->trunk_conf));
2749  inst->trunk_conf.req_pool_headers = 4; /* One for the request, one for the buffer, one for the tracking binding, one for Proxy-State VP */
2750  inst->trunk_conf.req_pool_size = sizeof(udp_request_t) + inst->max_packet_size + sizeof(radius_track_entry_t ***) + sizeof(fr_pair_t) + 20;
2751 
2752  return 0;
2753 }
2754 
2757  .common = {
2758  .magic = MODULE_MAGIC_INIT,
2759  .name = "radius_udp",
2760  .inst_size = sizeof(rlm_radius_udp_t),
2761 
2762  .thread_inst_size = sizeof(udp_thread_t),
2763  .thread_inst_type = "udp_thread_t",
2764 
2765  .config = module_config,
2766  .instantiate = mod_instantiate,
2767  .thread_instantiate = mod_thread_instantiate,
2768  },
2769  .enqueue = mod_enqueue,
2770  .signal = mod_signal,
2771  .resume = mod_resume,
2772 };
unlang_action_t
Returned by unlang_op_t calls, determine the next action of the interpreter.
Definition: action.h:35
@ UNLANG_ACTION_YIELD
Temporarily pause execution until an event occurs.
Definition: action.h:42
static int const char char buffer[256]
Definition: acutest.h:574
int const char * file
Definition: acutest.h:702
int const char int line
Definition: acutest.h:702
#define RCSID(id)
Definition: build.h:481
#define NDEBUG_UNUSED
Definition: build.h:324
#define CMP_PREFER_SMALLER(_a, _b)
Evaluates to +1 for a > b, and -1 for a < b.
Definition: build.h:102
#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:110
#define unlikely(_x)
Definition: build.h:379
#define UNUSED
Definition: build.h:313
#define CONF_PARSER_TERMINATOR
Definition: cf_parse.h:627
#define FR_INTEGER_BOUND_CHECK(_name, _var, _op, _bound)
Definition: cf_parse.h:487
#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_OFFSET_IS_SET(_name, _type, _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:282
#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
@ CONF_FLAG_REQUIRED
Error out if no matching CONF_PAIR is found, and no dflt value is set.
Definition: cf_parse.h:405
#define FR_CONF_OFFSET_TYPE_FLAGS(_name, _type, _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:241
Defines a CONF_PAIR to C data type mapping.
Definition: cf_parse.h:564
A section grouping multiple CONF_PAIR.
Definition: cf_priv.h:101
#define cf_log_err(_cf, _fmt,...)
Definition: cf_util.h:289
connection_state_t
Definition: connection.h:45
@ CONNECTION_STATE_FAILED
Connection has failed.
Definition: connection.h:54
@ CONNECTION_STATE_CONNECTED
File descriptor is open (ready for writing).
Definition: connection.h:52
@ CONNECTION_STATE_INIT
Init state, sets up connection.
Definition: connection.h:49
@ CONNECTION_STATE_CONNECTING
Waiting for connection to establish.
Definition: connection.h:50
@ CONNECTION_FAILED
Connection is being reconnected because it failed.
Definition: connection.h:84
Holds a complete set of functions for a connection.
Definition: connection.h:186
#define FR_DBUFF_TMP(_start, _len_or_end)
Creates a compound literal to pass into functions which accept a dbuff.
Definition: dbuff.h:514
fr_dcursor_eval_t void const * uctx
Definition: dcursor.h:546
#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:210
#define fr_assert_fail(_msg,...)
Calls panic_action ifndef NDEBUG, else logs error.
Definition: debug.h:216
@ FR_RADIUS_CODE_ACCESS_CHALLENGE
RFC2865 - Access-Challenge.
Definition: defs.h:43
@ FR_RADIUS_CODE_ACCESS_REQUEST
RFC2865 - Access-Request.
Definition: defs.h:33
@ FR_RADIUS_CODE_MAX
Maximum possible protocol code.
Definition: defs.h:53
@ FR_RADIUS_CODE_DISCONNECT_ACK
RFC3575/RFC5176 - Disconnect-Ack (positive)
Definition: defs.h:47
@ FR_RADIUS_CODE_STATUS_SERVER
RFC2865/RFC5997 - Status Server (request)
Definition: defs.h:44
@ FR_RADIUS_CODE_ACCESS_ACCEPT
RFC2865 - Access-Accept.
Definition: defs.h:34
@ FR_RADIUS_CODE_ACCOUNTING_RESPONSE
RFC2866 - Accounting-Response.
Definition: defs.h:37
@ FR_RADIUS_CODE_COA_NAK
RFC3575/RFC5176 - CoA-Nak (not willing to perform)
Definition: defs.h:51
@ FR_RADIUS_CODE_COA_ACK
RFC3575/RFC5176 - CoA-Ack (positive)
Definition: defs.h:50
@ FR_RADIUS_CODE_DISCONNECT_NAK
RFC3575/RFC5176 - Disconnect-Nak (not willing to perform)
Definition: defs.h:48
@ FR_RADIUS_CODE_PROTOCOL_ERROR
RFC7930 - Protocol-Error (generic NAK)
Definition: defs.h:52
@ FR_RADIUS_CODE_ACCESS_REJECT
RFC2865 - Access-Reject.
Definition: defs.h:35
#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:267
fr_dict_t const ** out
Where to write a pointer to the loaded/resolved fr_dict_t.
Definition: dict.h:280
Specifies an attribute which must be present for the module to function.
Definition: dict.h:266
Specifies a dictionary which must be loaded/loadable for the module to function.
Definition: dict.h:279
#define MODULE_MAGIC_INIT
Stop people using different module/library/server versions together.
Definition: dl_module.h:63
#define fr_event_fd_insert(...)
Definition: event.h:232
void(* fr_event_fd_cb_t)(fr_event_list_t *el, int fd, int flags, void *uctx)
Called when an IO event occurs on a file descriptor.
Definition: event.h:137
@ FR_EVENT_FILTER_IO
Combined filter for read/write functions/.
Definition: event.h:62
#define fr_event_timer_at(...)
Definition: event.h:250
IPv4/6 prefix.
Definition: merged_model.c:272
void unlang_interpret_mark_runnable(request_t *request)
Mark a request as resumable.
Definition: interpret.c:1359
Minimal data structure to use the new code.
Definition: listen.h:58
static bool fr_pair_encode_is_error(ssize_t slen)
Determine if the return code for an encoding function is a fatal error.
Definition: pair.h:74
void log_request_pair_list(fr_log_lvl_t lvl, request_t *request, fr_pair_t const *parent, fr_pair_list_t const *vps, char const *prefix)
Print a fr_pair_list_t.
Definition: log.c:830
#define PERROR(_fmt,...)
Definition: log.h:228
#define DEBUG3(_fmt,...)
Definition: log.h:266
#define RWDEBUG(fmt,...)
Definition: log.h:361
#define RWARN(fmt,...)
Definition: log.h:297
#define RERROR(fmt,...)
Definition: log.h:298
#define DEBUG4(_fmt,...)
Definition: log.h:267
#define RPERROR(fmt,...)
Definition: log.h:302
#define RINFO(fmt,...)
Definition: log.h:296
#define RPEDEBUG(fmt,...)
Definition: log.h:376
#define HEXDUMP3(_data, _len, _fmt,...)
Definition: log.h:723
#define RHEXDUMP3(_data, _len, _fmt,...)
Definition: log.h:705
int map_to_vp(TALLOC_CTX *ctx, fr_pair_list_t *out, request_t *request, map_t const *map, UNUSED void *uctx)
Convert a map to a fr_pair_t.
Definition: map.c:1487
int map_to_request(request_t *request, map_t const *map, radius_map_getvalue_t func, void *ctx)
Convert map_t to fr_pair_t (s) and add them to a request_t.
Definition: map.c:1781
talloc_free(reap)
int fr_event_timer_delete(fr_event_timer_t const **ev_p)
Delete a timer event from the event list.
Definition: event.c:1611
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:1260
Stores all information relating to an event list.
Definition: event.c:411
A timer event.
Definition: event.c:102
fr_log_t default_log
Definition: log.c:291
void fr_log(fr_log_t const *log, fr_log_type_t type, char const *file, int line, char const *fmt,...)
Send a server log message to its destination.
Definition: log.c:583
@ L_DBG_LVL_3
3rd highest priority debug messages (-xxx | -Xx).
Definition: log.h:72
@ L_DBG_LVL_2
2nd highest priority debug messages (-xx | -X).
Definition: log.h:71
fr_log_type_t
Definition: log.h:54
@ L_ERR
Error message.
Definition: log.h:56
fr_packet_t * fr_packet_alloc(TALLOC_CTX *ctx, bool new_vector)
Allocate a new fr_packet_t.
Definition: packet.c:38
unsigned short uint16_t
Definition: merged_model.c:31
@ FR_TYPE_IPV4_ADDR
32 Bit IPv4 Address.
Definition: merged_model.c:86
@ FR_TYPE_TLV
Contains nested attributes.
Definition: merged_model.c:118
@ FR_TYPE_STRING
String of printable characters.
Definition: merged_model.c:83
@ FR_TYPE_DATE
Unix time stamp, always has value >2^31.
Definition: merged_model.c:111
@ FR_TYPE_UINT32
32 Bit unsigned integer.
Definition: merged_model.c:99
@ FR_TYPE_IPV6_ADDR
128 Bit IPv6 Address.
Definition: merged_model.c:88
@ FR_TYPE_COMBO_IP_ADDR
IPv4 or IPv6 address depending on length.
Definition: merged_model.c:91
@ FR_TYPE_OCTETS
Raw octets.
Definition: merged_model.c:84
unsigned int uint32_t
Definition: merged_model.c:33
long int ssize_t
Definition: merged_model.c:24
unsigned char uint8_t
Definition: merged_model.c:30
unsigned long int size_t
Definition: merged_model.c:25
int sendmmsg(int sockfd, struct mmsghdr *msgvec, unsigned int vlen, int flags)
Emulates the real sendmmsg in userland.
Definition: missing.c:500
void * thread
Thread specific instance data.
Definition: module_ctx.h:43
void * rctx
Resume ctx that a module previously set.
Definition: module_ctx.h:45
fr_event_list_t * el
Event list to register any IO handlers and timers against.
Definition: module_ctx.h:68
void * thread
Thread instance data.
Definition: module_ctx.h:67
module_instance_t const * mi
Instance of the module being instantiated.
Definition: module_ctx.h:64
module_instance_t * mi
Instance of the module being instantiated.
Definition: module_ctx.h:51
Temporary structure to hold arguments for module calls.
Definition: module_ctx.h:41
Temporary structure to hold arguments for instantiation calls.
Definition: module_ctx.h:50
Temporary structure to hold arguments for thread_instantiation calls.
Definition: module_ctx.h:63
static uint16_t fr_nbo_to_uint16(uint8_t const data[static sizeof(uint16_t)])
Read an unsigned 16bit integer from wire format (big endian)
Definition: nbo.h:144
#define RADIUS_HEADER_LENGTH
Definition: net.h:80
#define RADIUS_AUTH_VECTOR_LENGTH
Definition: net.h:89
fr_pair_t * fr_pair_find_by_da(fr_pair_list_t const *list, fr_pair_t const *prev, fr_dict_attr_t const *da)
Find the first pair with a matching da.
Definition: pair.c:693
fr_pair_t * fr_pair_afrom_da(TALLOC_CTX *ctx, fr_dict_attr_t const *da)
Dynamically allocate a new attribute and assign a fr_dict_attr_t.
Definition: pair.c:283
int fr_pair_value_memdup(fr_pair_t *vp, uint8_t const *src, size_t len, bool tainted)
Copy data into an "octets" data type.
Definition: pair.c:2981
int fr_pair_value_strdup(fr_pair_t *vp, char const *src, bool tainted)
Copy data into an "string" data type.
Definition: pair.c:2634
int fr_pair_append(fr_pair_list_t *list, fr_pair_t *to_add)
Add a VP to the end of the list.
Definition: pair.c:1345
int fr_pair_delete_by_da(fr_pair_list_t *list, fr_dict_attr_t const *da)
Delete matching pairs from the specified list.
Definition: pair.c:1689
void fr_pair_list_init(fr_pair_list_t *list)
Initialise a pair list header.
Definition: pair.c:46
char * fr_asprintf(TALLOC_CTX *ctx, char const *fmt,...)
Special version of asprintf which implements custom format specifiers.
Definition: print.c:874
static fr_internal_encode_ctx_t encode_ctx
ssize_t fr_radius_decode(TALLOC_CTX *ctx, fr_pair_list_t *out, uint8_t *packet, size_t packet_len, fr_radius_decode_ctx_t *decode_ctx)
Definition: base.c:1087
int fr_radius_sign(uint8_t *packet, uint8_t const *vector, uint8_t const *secret, size_t secret_len)
Sign a previously encoded packet.
Definition: base.c:358
ssize_t fr_radius_encode(fr_dbuff_t *dbuff, fr_pair_list_t *vps, fr_radius_encode_ctx_t *packet_ctx)
Definition: base.c:952
char const * fr_radius_packet_name[FR_RADIUS_CODE_MAX]
Definition: base.c:112
static char * secret
Definition: radclient-ng.c:69
#define REDEBUG(fmt,...)
Definition: radclient.h:52
#define RDEBUG(fmt,...)
Definition: radclient.h:53
#define WARN(fmt,...)
Definition: radclient.h:47
#define INFO(fmt,...)
Definition: radict.c:54
@ FR_RADIUS_REQUIRE_MA_YES
Require Message-Authenticator.
Definition: radius.h:68
@ FR_RADIUS_REQUIRE_MA_AUTO
Only require Message-Authenticator if we've previously received a packet from this client with Messag...
Definition: radius.h:64
#define RADIUS_AUTH_VECTOR_OFFSET
Definition: radius.h:33
#define RADIUS_MAX_PACKET_SIZE
Definition: radius.h:41
fr_radius_ctx_t const * common
Definition: radius.h:125
TALLOC_CTX * tmp_ctx
for temporary things cleaned up during decoding
Definition: radius.h:129
static rs_t * conf
Definition: radsniff.c:53
uint32_t fr_rand(void)
Return a 32-bit random number.
Definition: rand.c:106
Smaller fast random number generator.
Definition: rand.h:54
#define RETURN_MODULE_NOOP
Definition: rcode.h:62
#define RETURN_MODULE_RCODE(_rcode)
Definition: rcode.h:64
rlm_rcode_t
Return codes indicating the result of the module call.
Definition: rcode.h:40
@ RLM_MODULE_OK
The module is OK, continue.
Definition: rcode.h:43
@ RLM_MODULE_FAIL
Module failed, don't reply.
Definition: rcode.h:42
@ RLM_MODULE_REJECT
Immediately reject the request.
Definition: rcode.h:41
@ RLM_MODULE_UPDATED
OK (pairs modified).
Definition: rcode.h:49
@ RLM_MODULE_HANDLED
The module handled the request, so stop.
Definition: rcode.h:44
#define request_local_alloc_external(_ctx, _args)
Allocate a new external request outside of the request pool.
Definition: request.h:316
fr_time_delta_t revive_interval
Definition: rlm_radius.h:49
module_t common
Common fields to all loadable modules.
Definition: rlm_radius.h:85
fr_retry_config_t retry[FR_RADIUS_CODE_MAX]
Definition: rlm_radius.h:70
char const * name
Definition: rlm_radius.h:43
uint32_t status_check
code of status-check type
Definition: rlm_radius.h:64
fr_time_delta_t response_window
Definition: rlm_radius.h:47
fr_time_delta_t zombie_period
Definition: rlm_radius.h:48
bool synchronous
Retransmit when receiving a duplicate request.
Definition: rlm_radius.h:52
Public structure describing an I/O path for an outgoing socket.
Definition: rlm_radius.h:84
uint32_t recv_buff
How big the kernel's receive buffer should be.
static int8_t request_prioritise(void const *one, void const *two)
static bool check_for_zombie(fr_event_list_t *el, trunk_connection_t *tconn, fr_time_t now, fr_time_t last_sent)
See if the connection is zombied.
static fr_dict_attr_t const * attr_packet_type
static fr_dict_attr_t const * attr_user_password
static int _udp_result_free(udp_result_t *r)
Free a udp_result_t.
trunk_conf_t trunk_conf
trunk configuration
static void request_complete(request_t *request, void *preq, void *rctx, UNUSED void *uctx)
Response has already been written to the rctx at this point.
static void status_check_alloc(udp_handle_t *h)
static int _udp_request_free(udp_request_t *u)
Free a udp_request_t.
static int encode(rlm_radius_udp_t const *inst, request_t *request, udp_request_t *u, uint8_t id)
uint16_t dst_port
Port of the home server.
static fr_dict_attr_t const * attr_eap_message
fr_ipaddr_t dst_ipaddr
IP of the home server.
static rlm_rcode_t radius_code_to_rcode[FR_RADIUS_CODE_MAX]
Turn a reply code into a module rcode;.
static void udp_request_reset(udp_request_t *u)
Clear out any connection specific resources from a udp request.
static void conn_error(UNUSED fr_event_list_t *el, UNUSED int fd, UNUSED int flags, int fd_errno, void *uctx)
Connection errored.
uint32_t send_buff
How big the kernel's send buffer should be.
static void mod_signal(module_ctx_t const *mctx, UNUSED request_t *request, fr_signal_t action)
static void conn_close(UNUSED fr_event_list_t *el, void *handle, UNUSED void *uctx)
Shutdown/close a file descriptor.
static void conn_writable_status_check(UNUSED fr_event_list_t *el, UNUSED int fd, UNUSED int flags, void *uctx)
static void request_conn_release(connection_t *conn, void *preq_to_reset, UNUSED void *uctx)
Clear out anything associated with the handle from the request.
static void request_fail(request_t *request, void *preq, void *rctx, NDEBUG_UNUSED trunk_request_state_t state, UNUSED void *uctx)
Write out a canned failure.
#define check(_handle, _len_p)
static void request_timeout(fr_event_list_t *el, fr_time_t now, void *uctx)
Handle timeouts when a request is being sent synchronously.
static void conn_error_status_check(UNUSED fr_event_list_t *el, UNUSED int fd, UNUSED int flags, int fd_errno, void *uctx)
Connection errored.
bool send_buff_is_set
Whether we were provided with a send_buf.
static fr_dict_t const * dict_radius
static void request_free(UNUSED request_t *request, void *preq_to_free, UNUSED void *uctx)
Explicitly free resources associated with the protocol request.
static void conn_status_check_timeout(fr_event_list_t *el, fr_time_t now, void *uctx)
Status check timer when opening the connection for the first time.
static void zombie_timeout(fr_event_list_t *el, fr_time_t now, void *uctx)
Mark a connection dead after "zombie_interval".
struct udp_request_s udp_request_t
fr_ipaddr_t src_ipaddr
IP we open our socket on.
static fr_dict_attr_t const * attr_extended_attribute_1
static void request_cancel(UNUSED connection_t *conn, void *preq_to_reset, trunk_cancel_reason_t reason, UNUSED void *uctx)
Remove the request from any tracking structures.
fr_dict_autoload_t rlm_radius_udp_dict[]
static void conn_status_check_again(fr_event_list_t *el, UNUSED fr_time_t now, void *uctx)
Send the next status check packet.
static fr_dict_attr_t const * attr_error_cause
static void status_check_next(UNUSED fr_event_list_t *el, UNUSED fr_time_t now, void *uctx)
Handle retries for a status check.
struct iovec out
Describes buffer to send.
static void revive_timeout(UNUSED fr_event_list_t *el, UNUSED fr_time_t now, void *uctx)
Revive a connection after "revive_interval".
static void conn_discard(UNUSED fr_event_list_t *el, int fd, UNUSED int flags, void *uctx)
Read and discard data.
fr_radius_ctx_t common_ctx
rlm_radius_t * parent
rlm_radius instance.
static fr_dict_attr_t const * attr_proxy_state
static void conn_readable_status_check(fr_event_list_t *el, UNUSED int fd, UNUSED int flags, void *uctx)
Read the incoming status-check response.
static fr_dict_attr_t const * attr_nas_identifier
char const * interface
Interface to bind to.
static int mod_thread_instantiate(module_thread_inst_ctx_t const *mctx)
Instantiate thread data for the submodule.
static void status_check_reply(trunk_request_t *treq, fr_time_t now)
Deal with replies replies to status checks and possible negotiation.
static unlang_action_t mod_resume(rlm_rcode_t *p_result, module_ctx_t const *mctx, UNUSED request_t *request)
Resume execution of the request, returning the rcode set during trunk execution.
static decode_fail_t decode(TALLOC_CTX *ctx, fr_pair_list_t *reply, uint8_t *response_code, udp_handle_t *h, request_t *request, udp_request_t *u, uint8_t const request_authenticator[static RADIUS_AUTH_VECTOR_LENGTH], uint8_t *data, size_t data_len)
Decode response packet data, extracting relevant information and validating the packet.
static void udp_tracking_entry_log(fr_log_t const *log, fr_log_type_t log_type, char const *file, int line, radius_track_entry_t *te)
Log additional information about a tracking entry.
static void protocol_error_reply(udp_request_t *u, udp_result_t *r, udp_handle_t *h)
Deal with Protocol-Error replies, and possible negotiation.
static void request_conn_release_replicate(UNUSED connection_t *conn, void *preq_to_reset, UNUSED void *uctx)
Clear out anything associated with the handle from the request.
static void status_check_retry(UNUSED fr_event_list_t *el, fr_time_t now, void *uctx)
static void request_retry(fr_event_list_t *el, fr_time_t now, void *uctx)
Handle retries when a request is being sent asynchronously.
trunk_request_t * treq
Used for signalling.
static unlang_action_t mod_enqueue(rlm_rcode_t *p_result, void **rctx_out, void *instance, void *thread, request_t *request)
static int _udp_handle_free(udp_handle_t *h)
Free a connection handle, closing associated resources.
uint32_t max_packet_size
Maximum packet size.
CONF_SECTION * config
CC_NO_UBSAN(function)
Initialise a new outbound connection.
char const * secret
Shared secret.
static const conf_parser_t module_config[]
fr_dict_attr_autoload_t rlm_radius_udp_dict_attr[]
bool replicate
Copied from parent->replicate.
static fr_dict_attr_t const * attr_original_packet_code
static void status_check_reset(udp_handle_t *h, udp_request_t *u)
Reset a status_check packet, ready to reuse.
static connection_state_t conn_failed(void *handle, connection_state_t state, UNUSED void *uctx)
Connection failed.
rlm_radius_io_t rlm_radius_udp
uint16_t max_send_coalesce
Maximum number of packets to coalesce into one mmsg call.
static fr_dict_attr_t const * attr_event_timestamp
static fr_dict_attr_t const * attr_response_length
static int mod_instantiate(module_inst_ctx_t const *mctx)
bool recv_buff_is_set
Whether we were provided with a recv_buf.
static fr_dict_attr_t const * attr_message_authenticator
Static configuration for the module.
udp_request_t * status_u
for sending status check packets
fr_time_t last_reply
When we last received a reply.
uint16_t src_port
Source port specific to this connection.
rlm_rcode_t rcode
from the transport
fr_time_t first_sent
first time we sent a packet since going idle
uint8_t * buffer
Receive buffer.
fr_event_list_t * el
Event list.
static void request_demux(UNUSED fr_event_list_t *el, trunk_connection_t *tconn, connection_t *conn, UNUSED void *uctx)
fr_time_t last_idle
last time we had nothing to do
size_t packet_len
Length of the packet.
uint8_t id
Last ID assigned to this packet.
fr_ipaddr_t src_ipaddr
Source IP address.
bool require_message_authenticator
saved from the original packet.
fr_pair_list_t extra
VPs for debugging, like Proxy-State.
fr_time_t recv_time
copied from request->async->recv_time
fr_event_timer_t const * ev
timer for retransmissions
fr_time_t last_sent
last time we sent a packet.
udp_result_t * status_r
for faking out status checks as real packets
rlm_radius_udp_t const * inst
our instance
size_t send_buff_actual
What we believe the maximum SO_SNDBUF size to be.
int fd
File descriptor.
struct mmsghdr * mmsgvec
Vector of inbound/outbound packets.
radius_track_t * tt
RADIUS ID tracking structure.
bool synchronous
cached from inst->parent->synchronous
trunk_t * trunk
trunk handler
radius_track_entry_t * rr
ID tracking, resend count, etc.
request_t * status_request
uint8_t * packet
Packet we write to the network.
char const * name
From IP PORT to IP PORT.
uint32_t max_packet_size
Our max packet size. may be different from the parent.
udp_coalesced_t * coalesced
Outbound coalesced requests.
bool status_check
is this packet a status check?
fr_time_t mrs_time
Most recent sent time which had a reply.
uint32_t num_replies
number of reply packets, sent is in retry.count
rlm_radius_udp_t const * inst
Our module instance.
uint8_t code
Packet code.
fr_event_timer_t const * zombie_ev
Zombie timeout.
bool status_checking
whether we're doing status checks
size_t buflen
Receive buffer length.
static connection_state_t conn_init(void **h_out, connection_t *conn, void *uctx)
Initialise a new outbound connection.
char const * module_name
the module that opened the connection
udp_thread_t * thread
fr_retry_t retry
retransmission timers
uint32_t priority
copied from request->async->priority
uint8_t last_id
Used when replicating to ensure IDs are distributed evenly.
trunk_request_t * treq
Track the handle, which is tightly correlated with the FD.
Connect request_t to local tracking structure.
void connection_signal_reconnect(connection_t *conn, connection_reason_t reason)
Asynchronously signal the connection should be reconnected.
Definition: connection.c:1167
int connection_signal_on_fd(connection_t *conn, int fd)
Setup the connection to change states to connected or failed based on I/O events.
Definition: connection.c:1405
connection_t * connection_alloc(TALLOC_CTX *ctx, fr_event_list_t *el, connection_funcs_t const *funcs, connection_conf_t const *conf, char const *log_prefix, void const *uctx)
Allocate a new connection.
Definition: connection.c:1512
void connection_signal_connected(connection_t *conn)
Asynchronously signal that the connection is open.
Definition: connection.c:1137
CONF_SECTION * conf
Module's instance configuration.
Definition: module.h:329
void * data
Module's instance data.
Definition: module.h:271
module_instance_t const * parent
Parent module's instance (if any).
Definition: module.h:337
#define pair_append_request(_attr, _da)
Allocate and append a fr_pair_t to the request list.
Definition: pair.h:37
#define pair_delete_request(_pair_or_da)
Delete a fr_pair_t in the request list.
Definition: pair.h:172
static fr_dict_attr_t const * tmpl_attr_tail_da(tmpl_t const *vpt)
Return the last attribute reference da.
Definition: tmpl.h:812
#define tmpl_is_attr(vpt)
Definition: tmpl.h:213
fr_signal_t
Definition: signal.h:48
int fr_socket_client_udp(char const *ifname, fr_ipaddr_t *src_ipaddr, uint16_t *src_port, fr_ipaddr_t const *dst_ipaddr, uint16_t dst_port, bool async)
Establish a connected UDP socket.
Definition: socket.c:634
RETURN_MODULE_FAIL
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
Definition: log.h:96
Value pair map.
Definition: map.h:77
tmpl_t * lhs
Typically describes the attribute to add, modify or compare.
Definition: map.h:78
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
#define talloc_get_type_abort_const
Definition: talloc.h:282
static int talloc_const_free(void const *ptr)
Free const'd memory.
Definition: talloc.h:224
@ DECODE_FAIL_UNKNOWN
Definition: test_point.h:38
@ DECODE_FAIL_NONE
Definition: test_point.h:24
#define fr_time_gteq(_a, _b)
Definition: time.h:238
static int64_t fr_time_unwrap(fr_time_t time)
Definition: time.h:146
#define fr_time_wrap(_time)
Definition: time.h:145
#define fr_time_lteq(_a, _b)
Definition: time.h:240
#define fr_time_delta_ispos(_a)
Definition: time.h:290
#define fr_time_eq(_a, _b)
Definition: time.h:241
#define fr_time_add(_a, _b)
Add a time/time delta together.
Definition: time.h:196
#define fr_time_gt(_a, _b)
Definition: time.h:237
#define fr_time_sub(_a, _b)
Subtract one time from another.
Definition: time.h:229
static fr_unix_time_t fr_time_to_unix_time(fr_time_t when)
Convert an fr_time_t (internal time) to our version of unix time (wallclock time)
Definition: time.h:688
#define fr_time_lt(_a, _b)
Definition: time.h:239
"server local" time.
Definition: time.h:69
void radius_track_state_log(fr_log_t const *log, fr_log_type_t log_type, char const *file, int line, radius_track_t *tt, radius_track_log_extra_t extra)
Print out the state of every tracking entry.
Definition: track.c:425
int radius_track_entry_update(radius_track_entry_t *te, uint8_t const *vector)
Update a tracking entry with the authentication vector.
Definition: track.c:293
radius_track_t * radius_track_alloc(TALLOC_CTX *ctx)
Create an radius_track_t.
Definition: track.c:42
radius_track_entry_t * radius_track_entry_find(radius_track_t *tt, uint8_t packet_id, uint8_t const *vector)
Find a tracking entry from a request authenticator.
Definition: track.c:338
#define radius_track_entry_release(_te)
Definition: track.h:95
void * uctx
Result/resumption context.
Definition: track.h:47
uint8_t id
our ID
Definition: track.h:50
unsigned int num_requests
number of requests in the allocation
Definition: track.h:65
#define radius_track_entry_reserve(_te_out, _ctx, _tt, _request, _code, _uctx)
Definition: track.h:87
request_t * request
as always...
Definition: track.h:45
Track one request to a response.
Definition: track.h:36
void trunk_connection_callback_readable(UNUSED fr_event_list_t *el, UNUSED int fd, UNUSED int flags, void *uctx)
Standard I/O read function.
Definition: trunk.c:3992
void trunk_connection_callback_writable(UNUSED fr_event_list_t *el, UNUSED int fd, UNUSED int flags, void *uctx)
Standard I/O write function.
Definition: trunk.c:4009
void trunk_request_signal_fail(trunk_request_t *treq)
Signal that a trunk request failed.
Definition: trunk.c:2120
uint64_t trunk_connection_requests_requeue(trunk_connection_t *tconn, int states, uint64_t max, bool fail_bound)
Move requests off of a connection and requeue elsewhere.
Definition: trunk.c:2003
trunk_enqueue_t trunk_request_enqueue_on_conn(trunk_request_t **treq_out, trunk_connection_t *tconn, request_t *request, void *preq, void *rctx, bool ignore_limits)
Enqueue additional requests on a specific connection.
Definition: trunk.c:2729
trunk_enqueue_t trunk_request_enqueue(trunk_request_t **treq_out, trunk_t *trunk, request_t *request, void *preq, void *rctx)
Enqueue a request that needs data written to the trunk.
Definition: trunk.c:2575
trunk_enqueue_t trunk_request_requeue(trunk_request_t *treq)
Re-enqueue a request on the same connection.
Definition: trunk.c:2664
int trunk_connection_pop_request(trunk_request_t **treq_out, trunk_connection_t *tconn)
Pop a request off a connection's pending queue.
Definition: trunk.c:3861
void trunk_request_signal_cancel(trunk_request_t *treq)
Cancel a trunk request.
Definition: trunk.c:2140
void trunk_request_free(trunk_request_t **treq_to_free)
If the trunk request is freed then update the target requests.
Definition: trunk.c:2310
trunk_request_t * trunk_request_alloc(trunk_t *trunk, request_t *request)
(Pre-)Allocate a new trunk request
Definition: trunk.c:2462
void trunk_connection_signal_active(trunk_connection_t *tconn)
Signal a trunk connection is no longer full.
Definition: trunk.c:3938
void trunk_connection_signal_inactive(trunk_connection_t *tconn)
Signal a trunk connection cannot accept more requests.
Definition: trunk.c:3915
void trunk_request_signal_sent(trunk_request_t *treq)
Signal that the request was written to a connection successfully.
Definition: trunk.c:2043
void trunk_request_signal_complete(trunk_request_t *treq)
Signal that a trunk request is complete.
Definition: trunk.c:2087
void trunk_connection_signal_reconnect(trunk_connection_t *tconn, connection_reason_t reason)
Signal a trunk connection is no longer viable.
Definition: trunk.c:3977
void trunk_request_state_log(fr_log_t const *log, fr_log_type_t log_type, char const *file, int line, trunk_request_t const *treq)
Definition: trunk.c:2816
trunk_t * trunk_alloc(TALLOC_CTX *ctx, fr_event_list_t *el, trunk_io_funcs_t const *funcs, trunk_conf_t const *conf, char const *log_prefix, void const *uctx, bool delay_start)
Allocate a new collection of connections.
Definition: trunk.c:4885
Associates request queues with a connection.
Definition: trunk.c:131
Wraps a normal request.
Definition: trunk.c:97
Main trunk management handle.
Definition: trunk.c:195
#define TRUNK_REQUEST_STATE_ALL
All request states.
Definition: trunk.h:195
trunk_connection_alloc_t connection_alloc
Allocate a new connection_t.
Definition: trunk.h:725
trunk_connection_event_t
What type of I/O events the trunk connection is currently interested in receiving.
Definition: trunk.h:72
@ TRUNK_CONN_EVENT_BOTH
Trunk should be notified if a connection is readable or writable.
Definition: trunk.h:79
@ TRUNK_CONN_EVENT_WRITE
Trunk should be notified if a connection is writable.
Definition: trunk.h:77
@ TRUNK_CONN_EVENT_NONE
Don't notify the trunk on connection state changes.
Definition: trunk.h:73
@ TRUNK_CONN_EVENT_READ
Trunk should be notified if a connection is readable.
Definition: trunk.h:75
trunk_cancel_reason_t
Reasons for a request being cancelled.
Definition: trunk.h:55
@ TRUNK_CANCEL_REASON_REQUEUE
A previously sent request is being requeued.
Definition: trunk.h:59
@ TRUNK_ENQUEUE_DST_UNAVAILABLE
Destination is down.
Definition: trunk.h:153
@ TRUNK_ENQUEUE_FAIL
General failure.
Definition: trunk.h:154
@ TRUNK_ENQUEUE_OK
Operation was successful.
Definition: trunk.h:150
@ TRUNK_ENQUEUE_NO_CAPACITY
At maximum number of connections, and no connection has capacity.
Definition: trunk.h:151
@ TRUNK_ENQUEUE_IN_BACKLOG
Request should be enqueued in backlog.
Definition: trunk.h:149
trunk_request_state_t
Used for sanity checks and to simplify freeing.
Definition: trunk.h:161
@ TRUNK_REQUEST_STATE_PARTIAL
Some of the request was written to the socket, more of it should be written later.
Definition: trunk.h:170
@ TRUNK_REQUEST_STATE_INIT
Initial state.
Definition: trunk.h:162
@ TRUNK_REQUEST_STATE_PENDING
In the queue of a connection and is pending writing.
Definition: trunk.h:168
@ TRUNK_REQUEST_STATE_SENT
Was written to a socket. Waiting for a response.
Definition: trunk.h:172
Common configuration parameters for a trunk.
Definition: trunk.h:224
I/O functions to pass to trunk_alloc.
Definition: trunk.h:724
close(uq->fd)
static fr_event_list_t * el
bool fr_pair_list_empty(fr_pair_list_t const *list)
Is a valuepair list empty.
Definition: pair_inline.c:125
void fr_pair_list_free(fr_pair_list_t *list)
Free memory used by a valuepair list.
Definition: pair_inline.c:113
void fr_pair_list_append(fr_pair_list_t *dst, fr_pair_list_t *src)
Appends a list of fr_pair_t from a temporary list to a destination list.
Definition: pair_inline.c:182
struct value_pair_s fr_pair_t
Definition: pair.h:48
static fr_slen_t parent
Definition: pair.h:851
fr_retry_state_t fr_retry_next(fr_retry_t *r, fr_time_t now)
Initialize a retransmission counter.
Definition: retry.c:108
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:53
fr_time_delta_t rt
retransmit interval
Definition: retry.h:57
uint32_t mrc
Maximum retransmission count.
Definition: retry.h:36
fr_retry_config_t const * config
master configuration
Definition: retry.h:52
@ FR_RETRY_MRC
reached maximum retransmission count
Definition: retry.h:47
@ FR_RETRY_CONTINUE
Definition: retry.h:46
@ FR_RETRY_MRD
reached maximum retransmission duration
Definition: retry.h:48
uint32_t count
number of sent packets
Definition: retry.h:58
fr_time_delta_t mrd
Maximum retransmission duration.
Definition: retry.h:35
fr_time_t updated
last update, really a cached "now".
Definition: retry.h:56
fr_time_t next
when the next timer should be set
Definition: retry.h:55
char const * fr_strerror(void)
Get the last library error.
Definition: strerror.c:554
#define fr_box_ipaddr(_val)
Definition: value.h:294
static fr_slen_t data
Definition: value.h:1265
#define fr_box_time_delta(_val)
Definition: value.h:343
int nonnull(2, 5))
static size_t char ** out
Definition: value.h:997