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: 8fe720681456fe47a9bbca30641313e2e59061a1 $
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: 8fe720681456fe47a9bbca30641313e2e59061a1 $")
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 
72  fr_trunk_conf_t *trunk_conf; //!< trunk configuration
74 
75 typedef struct {
76  fr_event_list_t *el; //!< Event list.
77 
78  rlm_radius_udp_t const *inst; //!< our instance
79 
80  fr_trunk_t *trunk; //!< trunk handler
81 } udp_thread_t;
82 
83 typedef struct {
85  rlm_rcode_t rcode; //!< from the transport
86 } udp_result_t;
87 
88 typedef struct udp_request_s udp_request_t;
89 
90 typedef struct {
91  struct iovec out; //!< Describes buffer to send.
92  fr_trunk_request_t *treq; //!< Used for signalling.
94 
95 /** Track the handle, which is tightly correlated with the FD
96  *
97  */
98 typedef struct {
99  char const *name; //!< From IP PORT to IP PORT.
100  char const *module_name; //!< the module that opened the connection
101 
102  int fd; //!< File descriptor.
103 
104  struct mmsghdr *mmsgvec; //!< Vector of inbound/outbound packets.
105  udp_coalesced_t *coalesced; //!< Outbound coalesced requests.
106 
107  size_t send_buff_actual; //!< What we believe the maximum SO_SNDBUF size to be.
108  ///< We don't try and encode more packet data than this
109  ///< in one go.
110 
111  rlm_radius_udp_t const *inst; //!< Our module instance.
113 
114  uint8_t last_id; //!< Used when replicating to ensure IDs are distributed
115  ///< evenly.
116 
117  uint32_t max_packet_size; //!< Our max packet size. may be different from the parent.
118 
119  fr_ipaddr_t src_ipaddr; //!< Source IP address. May be altered on bind
120  //!< to be the actual IP address packets will be
121  //!< sent on. This is why we can't use the inst
122  //!< src_ipaddr field.
123  uint16_t src_port; //!< Source port specific to this connection.
124 
125  uint8_t *buffer; //!< Receive buffer.
126  size_t buflen; //!< Receive buffer length.
127 
128  radius_track_t *tt; //!< RADIUS ID tracking structure.
129 
130  fr_time_t mrs_time; //!< Most recent sent time which had a reply.
131  fr_time_t last_reply; //!< When we last received a reply.
132  fr_time_t first_sent; //!< first time we sent a packet since going idle
133  fr_time_t last_sent; //!< last time we sent a packet.
134  fr_time_t last_idle; //!< last time we had nothing to do
135 
136  fr_event_timer_t const *zombie_ev; //!< Zombie timeout.
137 
138  bool status_checking; //!< whether we're doing status checks
139  udp_request_t *status_u; //!< for sending status check packets
140  udp_result_t *status_r; //!< for faking out status checks as real packets
142 } udp_handle_t;
143 
144 
145 /** Connect request_t to local tracking structure
146  *
147  */
149  uint32_t priority; //!< copied from request->async->priority
150  fr_time_t recv_time; //!< copied from request->async->recv_time
151 
152  uint32_t num_replies; //!< number of reply packets, sent is in retry.count
153 
154  bool synchronous; //!< cached from inst->parent->synchronous
155  bool require_ma; //!< saved from the original packet.
156  bool can_retransmit; //!< can we retransmit this packet?
157  bool status_check; //!< is this packet a status check?
158 
159  fr_pair_list_t extra; //!< VPs for debugging, like Proxy-State.
160 
161  uint8_t code; //!< Packet code.
162  uint8_t id; //!< Last ID assigned to this packet.
163  uint8_t *packet; //!< Packet we write to the network.
164  size_t packet_len; //!< Length of the packet.
165 
166  radius_track_entry_t *rr; //!< ID tracking, resend count, etc.
167  fr_event_timer_t const *ev; //!< timer for retransmissions
168  fr_retry_t retry; //!< retransmission timers
169 };
170 
171 static const conf_parser_t module_config[] = {
172  { FR_CONF_OFFSET_TYPE_FLAGS("ipaddr", FR_TYPE_COMBO_IP_ADDR, 0, rlm_radius_udp_t, dst_ipaddr), },
173  { FR_CONF_OFFSET_TYPE_FLAGS("ipv4addr", FR_TYPE_IPV4_ADDR, 0, rlm_radius_udp_t, dst_ipaddr) },
174  { FR_CONF_OFFSET_TYPE_FLAGS("ipv6addr", FR_TYPE_IPV6_ADDR, 0, rlm_radius_udp_t, dst_ipaddr) },
175 
176  { FR_CONF_OFFSET("port", rlm_radius_udp_t, dst_port) },
177 
179 
180  { FR_CONF_OFFSET("interface", rlm_radius_udp_t, interface) },
181 
182  { FR_CONF_OFFSET_IS_SET("recv_buff", FR_TYPE_UINT32, 0, rlm_radius_udp_t, recv_buff) },
183  { FR_CONF_OFFSET_IS_SET("send_buff", FR_TYPE_UINT32, 0, rlm_radius_udp_t, send_buff) },
184 
185  { FR_CONF_OFFSET("max_packet_size", rlm_radius_udp_t, max_packet_size), .dflt = "4096" },
186  { FR_CONF_OFFSET("max_send_coalesce", rlm_radius_udp_t, max_send_coalesce), .dflt = "1024" },
187 
188  { FR_CONF_OFFSET_TYPE_FLAGS("src_ipaddr", FR_TYPE_COMBO_IP_ADDR, 0, rlm_radius_udp_t, src_ipaddr) },
189  { FR_CONF_OFFSET_TYPE_FLAGS("src_ipv4addr", FR_TYPE_IPV4_ADDR, 0, rlm_radius_udp_t, src_ipaddr) },
190  { FR_CONF_OFFSET_TYPE_FLAGS("src_ipv6addr", FR_TYPE_IPV6_ADDR, 0, rlm_radius_udp_t, src_ipaddr) },
191 
193 };
194 
195 static fr_dict_t const *dict_radius;
196 
199  { .out = &dict_radius, .proto = "radius" },
200  { NULL }
201 };
202 
214 
217  { .out = &attr_acct_delay_time, .name = "Acct-Delay-Time", .type = FR_TYPE_UINT32, .dict = &dict_radius},
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_nas_identifier, .name = "NAS-Identifier", .type = FR_TYPE_STRING, .dict = &dict_radius},
223  { .out = &attr_original_packet_code, .name = "Extended-Attribute-1.Original-Packet-Code", .type = FR_TYPE_UINT32, .dict = &dict_radius},
224  { .out = &attr_proxy_state, .name = "Proxy-State", .type = FR_TYPE_OCTETS, .dict = &dict_radius},
225  { .out = &attr_response_length, .name = "Extended-Attribute-1.Response-Length", .type = FR_TYPE_UINT32, .dict = &dict_radius },
226  { .out = &attr_user_password, .name = "User-Password", .type = FR_TYPE_STRING, .dict = &dict_radius},
227  { .out = &attr_packet_type, .name = "Packet-Type", .type = FR_TYPE_UINT32, .dict = &dict_radius },
228  { NULL }
229 };
230 
231 /** Turn a reply code into a module rcode;
232  *
233  */
238 
240 
243 
246 
248 };
249 
251  UNUSED int flags, void *uctx);
252 
253 static int encode(rlm_radius_udp_t const *inst, request_t *request, udp_request_t *u, uint8_t id);
254 
255 static decode_fail_t decode(TALLOC_CTX *ctx, fr_pair_list_t *reply, uint8_t *response_code,
256  udp_handle_t *h, request_t *request, udp_request_t *u,
257  uint8_t const request_authenticator[static RADIUS_AUTH_VECTOR_LENGTH],
258  uint8_t *data, size_t data_len);
259 
261 
262 #ifndef NDEBUG
263 /** Log additional information about a tracking entry
264  *
265  * @param[in] te Tracking entry we're logging information for.
266  * @param[in] log destination.
267  * @param[in] log_type Type of log message.
268  * @param[in] file the logging request was made in.
269  * @param[in] line logging request was made on.
270  */
271 static void udp_tracking_entry_log(fr_log_t const *log, fr_log_type_t log_type, char const *file, int line,
273 {
274  request_t *request;
275 
276  if (!te->request) return; /* Free entry */
277 
278  request = talloc_get_type_abort(te->request, request_t);
279 
280  fr_log(log, log_type, file, line, "request %s, allocated %s:%u", request->name,
281  request->alloc_file, request->alloc_line);
282 
283  fr_trunk_request_state_log(log, log_type, file, line, talloc_get_type_abort(te->uctx, fr_trunk_request_t));
284 }
285 #endif
286 
287 /** Clear out any connection specific resources from a udp request
288  *
289  */
291 {
292  TALLOC_FREE(u->packet);
293  fr_pair_list_init(&u->extra); /* Freed with packet */
294 
295  /*
296  * Can have packet put no u->rr
297  * if this is part of a pre-trunk status check.
298  */
299  if (u->rr) radius_track_entry_release(&u->rr);
300  u->can_retransmit = false;
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  fr_connection_t *conn = talloc_get_type_abort(uctx, fr_connection_t);
435  udp_handle_t *h;
436 
437  /*
438  * Connection must be in the connecting state when this fires
439  */
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  fr_connection_t *conn = talloc_get_type_abort(uctx, fr_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  */
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  fr_connection_t *conn = talloc_get_type_abort(uctx, fr_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  fr_connection_t *conn = talloc_get_type_abort(uctx, fr_connection_t);
518  udp_handle_t *h = talloc_get_type_abort(conn->h, udp_handle_t);
519  fr_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  fr_connection_t *conn = talloc_get_type_abort(uctx, fr_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 length %ld over connection %s",
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 static fr_connection_state_t conn_init(void **h_out, fr_connection_t *conn, void *uctx)
713 {
714  int fd;
715  udp_handle_t *h;
716  udp_thread_t *thread = talloc_get_type_abort(uctx, udp_thread_t);
717  uint16_t i;
718 
719  MEM(h = talloc_zero(conn, udp_handle_t));
720  h->thread = thread;
721  h->inst = thread->inst;
722  h->module_name = h->inst->parent->name;
723  h->src_ipaddr = h->inst->src_ipaddr;
724  h->src_port = 0;
726  h->last_idle = fr_time();
727 
728  /*
729  * mmsgvec is pre-populated with pointers
730  * to the iovec structs in coalesced, so we
731  * just need to setup the iovec, and pass how
732  * many messages we want to send to sendmmsg.
733  */
734  h->mmsgvec = talloc_zero_array(h, struct mmsghdr, h->inst->max_send_coalesce);
735  h->coalesced = talloc_zero_array(h, udp_coalesced_t, h->inst->max_send_coalesce);
736  for (i = 0; i < h->inst->max_send_coalesce; i++) {
737  h->mmsgvec[i].msg_hdr.msg_iov = &h->coalesced[i].out;
738  h->mmsgvec[i].msg_hdr.msg_iovlen = 1;
739  }
740 
741  MEM(h->buffer = talloc_array(h, uint8_t, h->max_packet_size));
742  h->buflen = h->max_packet_size;
743 
744  if (!h->inst->replicate) MEM(h->tt = radius_track_alloc(h));
745 
746  /*
747  * Open the outgoing socket.
748  */
750  &h->inst->dst_ipaddr, h->inst->dst_port, true);
751  if (fd < 0) {
752  PERROR("%s - Failed opening socket", h->module_name);
753  fail:
754  talloc_free(h);
756  }
757 
758  /*
759  * Set the connection name.
760  */
761  h->name = fr_asprintf(h, "proto udp local %pV port %u remote %pV port %u",
764 
765  talloc_set_destructor(h, _udp_handle_free);
766 
767 #ifdef SO_RCVBUF
768  if (h->inst->recv_buff_is_set) {
769  int opt;
770 
771  opt = h->inst->recv_buff;
772  if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(int)) < 0) {
773  WARN("%s - Failed setting 'SO_RCVBUF': %s", h->module_name, fr_syserror(errno));
774  }
775  }
776 #endif
777 
778 #ifdef SO_SNDBUF
779  {
780  int opt;
781  socklen_t socklen = sizeof(int);
782 
783  if (h->inst->send_buff_is_set) {
784  opt = h->inst->send_buff;
785  if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &opt, sizeof(int)) < 0) {
786  WARN("%s - Failed setting 'SO_SNDBUF', write performance may be sub-optimal: %s",
787  h->module_name, fr_syserror(errno));
788  }
789  }
790 
791  if (getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &opt, &socklen) < 0) {
792  WARN("%s - Failed getting 'SO_SNDBUF', write performance may be sub-optimal: %s",
793  h->module_name, fr_syserror(errno));
794 
795  /*
796  * This controls how many packets we attempt
797  * to send at once. Nothing bad happens if
798  * we get it wrong, but the user may see
799  * ENOBUFS errors at high packet rates.
800  */
803 
804  WARN("%s - Max coalesced outbound data will be %zu bytes", h->module_name,
805  h->send_buff_actual);
806  } else {
807 #ifdef __linux__
808  /*
809  * Linux doubles the buffer when you set it
810  * to account for "overhead".
811  */
812  h->send_buff_actual = ((size_t)opt) / 2;
813 #else
814  h->send_buff_actual = (size_t)opt;
815 #endif
816  }
817  }
818 #else
820  h->inst_send_buff : h->max_packet_size * h->inst->max_send_coalesce;
821 
822  WARN("%s - Modifying 'SO_SNDBUF' value is not supported on this system, "
823  "write performance may be sub-optimal", h->module_name);
824  WARN("%s - Max coalesced outbound data will be %zu bytes", h->module_name, h->inst->send_buff_actual);
825 #endif
826 
827  h->fd = fd;
828 
829  /*
830  * If we're doing status checks, then we want at least
831  * one positive response before signalling that the
832  * connection is open.
833  *
834  * To do this we install special I/O handlers that
835  * only signal the connection as open once we get a
836  * status-check response.
837  */
838  if (h->inst->parent->status_check) {
840 
841  /*
842  * Start status checking.
843  *
844  * If we've had no recent failures we need exactly
845  * one response to bring the connection online,
846  * otherwise we need inst->num_answers_to_alive
847  */
848  if (fr_event_fd_insert(h, NULL, conn->el, h->fd, NULL,
850  /*
851  * If we're not doing status-checks, signal the connection
852  * as open as soon as it becomes writable.
853  */
854  } else {
855  fr_connection_signal_on_fd(conn, fd);
856  }
857 
858  *h_out = h;
859 
860  // @todo - initialize the tracking memory, etc.
861  // i.e. histograms (or hyperloglog) of packets, so we can see
862  // which connections / home servers are fast / slow.
863 
865 }
866 
867 /** Shutdown/close a file descriptor
868  *
869  */
870 static void conn_close(UNUSED fr_event_list_t *el, void *handle, UNUSED void *uctx)
871 {
872  udp_handle_t *h = talloc_get_type_abort(handle, udp_handle_t);
873 
874  /*
875  * There's tracking entries still allocated
876  * this is bad, they should have all been
877  * released.
878  */
879  if (h->tt && (h->tt->num_requests != 0)) {
880 #ifndef NDEBUG
882 #endif
883  fr_assert_fail("%u tracking entries still allocated at conn close", h->tt->num_requests);
884  }
885 
886  DEBUG4("Freeing rlm_radius_udp handle %p", handle);
887 
888  talloc_free(h);
889 }
890 
891 /** Connection failed
892  *
893  * @param[in] handle of connection that failed.
894  * @param[in] state the connection was in when it failed.
895  * @param[in] uctx UNUSED.
896  */
897 static fr_connection_state_t conn_failed(void *handle, fr_connection_state_t state, UNUSED void *uctx)
898 {
899  switch (state) {
900  /*
901  * If the connection was connected when it failed,
902  * we need to handle any outstanding packets and
903  * timer events before reconnecting.
904  */
906  {
907  udp_handle_t *h = talloc_get_type_abort(handle, udp_handle_t); /* h only available if connected */
908 
909  /*
910  * Reset the Status-Server checks.
911  */
912  if (h->status_u && h->status_u->ev) (void) fr_event_timer_delete(&h->status_u->ev);
913  }
914  break;
915 
916  default:
917  break;
918  }
919 
921 }
922 
924  fr_connection_conf_t const *conf,
925  char const *log_prefix, void *uctx)
926 {
927  fr_connection_t *conn;
928  udp_thread_t *thread = talloc_get_type_abort(uctx, udp_thread_t);
929 
930  conn = fr_connection_alloc(tconn, el,
932  .init = conn_init,
933  .close = conn_close,
934  .failed = conn_failed
935  },
936  conf,
937  log_prefix,
938  thread);
939  if (!conn) {
940  PERROR("%s - Failed allocating state handler for new connection", thread->inst->parent->name);
941  return NULL;
942  }
943 
944  return conn;
945 }
946 
947 /** Read and discard data
948  *
949  */
950 static void conn_discard(UNUSED fr_event_list_t *el, int fd, UNUSED int flags, void *uctx)
951 {
952  fr_trunk_connection_t *tconn = talloc_get_type_abort(uctx, fr_trunk_connection_t);
953  udp_handle_t *h = talloc_get_type_abort(tconn->conn->h, udp_handle_t);
954  uint8_t buffer[4096];
955  ssize_t slen;
956 
957  while ((slen = read(fd, buffer, sizeof(buffer))) > 0);
958 
959  if (slen < 0) {
960  switch (errno) {
961  case EBADF:
962  case ECONNRESET:
963  case ENOTCONN:
964  case ETIMEDOUT:
965  ERROR("%s - Failed draining socket: %s", h->module_name, fr_syserror(errno));
967  break;
968 
969  default:
970  break;
971  }
972  }
973 }
974 
975 /** Connection errored
976  *
977  * We were signalled by the event loop that a fatal error occurred on this connection.
978  *
979  * @param[in] el The event list signalling.
980  * @param[in] fd that errored.
981  * @param[in] flags El flags.
982  * @param[in] fd_errno The nature of the error.
983  * @param[in] uctx The trunk connection handle (tconn).
984  */
985 static void conn_error(UNUSED fr_event_list_t *el, UNUSED int fd, UNUSED int flags, int fd_errno, void *uctx)
986 {
987  fr_trunk_connection_t *tconn = talloc_get_type_abort(uctx, fr_trunk_connection_t);
988  fr_connection_t *conn = tconn->conn;
989  udp_handle_t *h = talloc_get_type_abort(conn->h, udp_handle_t);
990 
991  ERROR("%s - Connection %s failed: %s", h->module_name, h->name, fr_syserror(fd_errno));
992 
994 }
995 
998  fr_trunk_connection_event_t notify_on, UNUSED void *uctx)
999 {
1000  udp_handle_t *h = talloc_get_type_abort(conn->h, udp_handle_t);
1001  fr_event_fd_cb_t read_fn = NULL;
1002  fr_event_fd_cb_t write_fn = NULL;
1003 
1004  switch (notify_on) {
1005  /*
1006  * We may have sent multiple requests to the
1007  * other end, so it might be sending us multiple
1008  * replies. We want to drain the socket, instead
1009  * of letting the packets sit in the UDP receive
1010  * queue.
1011  */
1013  read_fn = conn_discard;
1014  break;
1015 
1018  break;
1019 
1022  break;
1023 
1027  break;
1028 
1029  }
1030 
1031  if (fr_event_fd_insert(h, NULL, el, h->fd,
1032  read_fn,
1033  write_fn,
1034  conn_error,
1035  tconn) < 0) {
1036  PERROR("%s - Failed inserting FD event", h->module_name);
1037 
1038  /*
1039  * May free the connection!
1040  */
1042  }
1043 }
1044 
1045 /** A special version of the trunk/event loop glue function which always discards incoming data
1046  *
1047  */
1050  fr_trunk_connection_event_t notify_on, UNUSED void *uctx)
1051 {
1052  udp_handle_t *h = talloc_get_type_abort(conn->h, udp_handle_t);
1053  fr_event_fd_cb_t read_fn = NULL;
1054  fr_event_fd_cb_t write_fn = NULL;
1055 
1056  switch (notify_on) {
1058  read_fn = conn_discard;
1059  write_fn = NULL;
1060  break;
1061 
1063  read_fn = conn_discard;
1064  break;
1065 
1068  read_fn = conn_discard;
1070  break;
1071  }
1072 
1073  if (fr_event_fd_insert(h, NULL, el, h->fd,
1074  read_fn,
1075  write_fn,
1076  conn_error,
1077  tconn) < 0) {
1078  PERROR("%s - Failed inserting FD event", h->module_name);
1079 
1080  /*
1081  * May free the connection!
1082  */
1084  }
1085 }
1086 
1087 /*
1088  * Return negative numbers to put 'a' at the top of the heap.
1089  * Return positive numbers to put 'b' at the top of the heap.
1090  *
1091  * We want the value with the lowest timestamp to be prioritized at
1092  * the top of the heap.
1093  */
1094 static int8_t request_prioritise(void const *one, void const *two)
1095 {
1096  udp_request_t const *a = one;
1097  udp_request_t const *b = two;
1098  int8_t ret;
1099 
1100  // @todo - prioritize packets if there's a state?
1101 
1102  /*
1103  * Prioritise status check packets
1104  */
1105  ret = (b->status_check - a->status_check);
1106  if (ret != 0) return ret;
1107 
1108  /*
1109  * Larger priority is more important.
1110  */
1111  ret = CMP(a->priority, b->priority);
1112  if (ret != 0) return ret;
1113 
1114  /*
1115  * Smaller timestamp (i.e. earlier) is more important.
1116  */
1118 }
1119 
1120 /** Decode response packet data, extracting relevant information and validating the packet
1121  *
1122  * @param[in] ctx to allocate pairs in.
1123  * @param[out] reply Pointer to head of pair list to add reply attributes to.
1124  * @param[out] response_code The type of response packet.
1125  * @param[in] h connection handle.
1126  * @param[in] request the request.
1127  * @param[in] u UDP request.
1128  * @param[in] request_authenticator from the original request.
1129  * @param[in] data to decode.
1130  * @param[in] data_len Length of input data.
1131  * @return
1132  * - DECODE_FAIL_NONE on success.
1133  * - DECODE_FAIL_* on failure.
1134  */
1135 static decode_fail_t decode(TALLOC_CTX *ctx, fr_pair_list_t *reply, uint8_t *response_code,
1136  udp_handle_t *h, request_t *request, udp_request_t *u,
1137  uint8_t const request_authenticator[static RADIUS_AUTH_VECTOR_LENGTH],
1138  uint8_t *data, size_t data_len)
1139 {
1140  rlm_radius_udp_t const *inst = h->thread->inst;
1141  uint8_t code;
1142  fr_radius_ctx_t common_ctx;
1143  fr_radius_decode_ctx_t decode_ctx;
1144 
1145  *response_code = 0; /* Initialise to keep the rest of the code happy */
1146 
1147  RHEXDUMP3(data, data_len, "Read packet");
1148 
1149  common_ctx = (fr_radius_ctx_t) {
1150  .secret = inst->secret,
1151  .secret_length = talloc_array_length(inst->secret) - 1,
1152  };
1153 
1154  decode_ctx = (fr_radius_decode_ctx_t) {
1155  .common = &common_ctx,
1156  .request_code = u->code,
1157  .request_authenticator = request_authenticator,
1158  .tmp_ctx = talloc(ctx, uint8_t),
1159  .end = data + data_len,
1160  .verify = true,
1161  };
1162 
1163  /*
1164  * !client->active means a fake packet defining a dynamic client - so there will
1165  * be no secret defined yet - so can't verify.
1166  */
1167  if (fr_radius_decode(request->request_ctx, &request->request_pairs,
1168  data, data_len, &decode_ctx) < 0) {
1169  talloc_free(decode_ctx.tmp_ctx);
1170  RPEDEBUG("Failed reading packet");
1171  return DECODE_FAIL_UNKNOWN;
1172  }
1173  talloc_free(decode_ctx.tmp_ctx);
1174 
1175  code = data[0];
1176 
1177  RDEBUG("Received %s ID %d length %ld reply packet on connection %s",
1178  fr_radius_packet_name[code], data[1], data_len, h->name);
1179  log_request_pair_list(L_DBG_LVL_2, request, NULL, reply, NULL);
1180 
1181  *response_code = code;
1182 
1183  /*
1184  * Record the fact we've seen a response
1185  */
1186  u->num_replies++;
1187 
1188  /*
1189  * Fixup retry times
1190  */
1191  if (fr_time_gt(u->retry.start, h->mrs_time)) h->mrs_time = u->retry.start;
1192 
1193  return DECODE_FAIL_NONE;
1194 }
1195 
1196 static int encode(rlm_radius_udp_t const *inst, request_t *request, udp_request_t *u, uint8_t id)
1197 {
1198  ssize_t packet_len;
1199  uint8_t *msg = NULL;
1200  int message_authenticator = u->require_ma * (RADIUS_MESSAGE_AUTHENTICATOR_LENGTH + 2);
1201  int proxy_state = 6;
1202 
1203  fr_assert(inst->parent->allowed[u->code]);
1204  fr_assert(!u->packet);
1205 
1206  /*
1207  * Try to retransmit, unless there are special
1208  * circumstances.
1209  */
1210  u->can_retransmit = true;
1211 
1212  /*
1213  * This is essentially free, as this memory was
1214  * pre-allocated as part of the treq.
1215  */
1216  u->packet_len = inst->max_packet_size;
1217  MEM(u->packet = talloc_array(u, uint8_t, u->packet_len));
1218 
1219  /*
1220  * All proxied Access-Request packets MUST have a
1221  * Message-Authenticator, otherwise they're insecure.
1222  * Same goes for Status-Server.
1223  *
1224  * And we set the authentication vector to a random
1225  * number...
1226  */
1227  switch (u->code) {
1230  {
1231  size_t i;
1232  uint32_t hash, base;
1233 
1234  message_authenticator = RADIUS_MESSAGE_AUTHENTICATOR_LENGTH + 2;
1235 
1236  base = fr_rand();
1237  for (i = 0; i < RADIUS_AUTH_VECTOR_LENGTH; i += sizeof(uint32_t)) {
1238  hash = fr_rand() ^ base;
1239  memcpy(u->packet + RADIUS_AUTH_VECTOR_OFFSET + i, &hash, sizeof(hash));
1240  }
1241  }
1242  FALL_THROUGH;
1243 
1244  default:
1245  break;
1246  }
1247 
1248 
1249  /*
1250  * If we're sending a status check packet, update any
1251  * necessary timestamps. Also, don't add Proxy-State, as
1252  * we're originating the packet.
1253  */
1254  if (u->status_check) {
1255  fr_pair_t *vp;
1256 
1257  proxy_state = 0;
1258  vp = fr_pair_find_by_da(&request->request_pairs, NULL, attr_event_timestamp);
1259  if (vp) vp->vp_date = fr_time_to_unix_time(u->retry.updated);
1260 
1261  if (u->code == FR_RADIUS_CODE_STATUS_SERVER) u->can_retransmit = false;
1262 
1263  } else if (inst->parent->originate) {
1264  /*
1265  * We're originating packets instead of proxying
1266  * them. We don't add a Proxy-State attribute.
1267  */
1268  proxy_state = 0;
1269  }
1270 
1271  /*
1272  * We should have at minimum 64-byte packets, so don't
1273  * bother doing run-time checks here.
1274  */
1275  fr_assert(u->packet_len >= (size_t) (RADIUS_HEADER_LENGTH + proxy_state + message_authenticator));
1276 
1277  /*
1278  * Encode it, leaving room for Proxy-State and
1279  * Message-Authenticator if necessary.
1280  */
1281  packet_len = fr_radius_encode(u->packet, u->packet_len - (proxy_state + message_authenticator), NULL,
1282  inst->secret, talloc_array_length(inst->secret) - 1,
1283  u->code, id, &request->request_pairs);
1284  if (fr_pair_encode_is_error(packet_len)) {
1285  RPERROR("Failed encoding packet");
1286 
1287  error:
1288  TALLOC_FREE(u->packet);
1289  return -1;
1290  }
1291 
1292  if (packet_len < 0) {
1293  size_t have;
1294  size_t need;
1295 
1296  have = u->packet_len - (proxy_state + message_authenticator);
1297  need = have - packet_len;
1298 
1299  if (need > RADIUS_MAX_PACKET_SIZE) {
1300  RERROR("Failed encoding packet. Have %zu bytes of buffer, need %zu bytes",
1301  have, need);
1302  } else {
1303  RERROR("Failed encoding packet. Have %zu bytes of buffer, need %zu bytes. "
1304  "Increase 'max_packet_size'", have, need);
1305  }
1306 
1307  goto error;
1308  }
1309  /*
1310  * The encoded packet should NOT over-run the input buffer.
1311  */
1312  fr_assert((size_t) (packet_len + proxy_state + message_authenticator) <= u->packet_len);
1313 
1314  /*
1315  * Add Proxy-State to the tail end of the packet.
1316  *
1317  * We need to add it here, and NOT in
1318  * request->request_pairs, because multiple modules
1319  * may be sending the packets at the same time.
1320  */
1321  if (proxy_state) {
1322  uint8_t *attr = u->packet + packet_len;
1323  fr_pair_t *vp;
1324  fr_dcursor_t cursor;
1325  int count = 0;
1326 
1327  /*
1328  * Count how many Proxy-State attributes have
1329  * *our* magic number. Note that we also add a
1330  * counter to each Proxy-State, so we're double
1331  * sure that it's a loop.
1332  */
1333  if (DEBUG_ENABLED) {
1334  for (vp = fr_pair_dcursor_by_da_init(&cursor, &request->request_pairs, attr_proxy_state);
1335  vp;
1336  vp = fr_dcursor_next(&cursor)) {
1337  if ((vp->vp_length == 5) && (memcmp(vp->vp_octets, &inst->parent->proxy_state, 4) == 0)) {
1338  count++;
1339  }
1340  }
1341 
1342  /*
1343  * Some configurations may proxy to
1344  * ourselves for tests / simplicity. But
1345  * warn if there are a large number of
1346  * identical Proxy-State attributes.
1347  */
1348  if (count >= 4) RWARN("Potential proxy loop detected! Please recheck your configuration.");
1349  }
1350 
1351  attr[0] = (uint8_t)attr_proxy_state->attr;
1352  attr[1] = 7;
1353  memcpy(attr + 2, &inst->parent->proxy_state, 4);
1354  attr[6] = count & 0xff;
1355  packet_len += 7;
1356 
1358  fr_pair_value_memdup(vp, attr + 2, 5, true);
1359  fr_pair_append(&u->extra, vp);
1360  }
1361 
1362  /*
1363  * Add Message-Authenticator manually.
1364  *
1365  * Note that the length check will always pass, due to
1366  * the buflen manipulation done above.
1367  */
1368  if (message_authenticator) {
1369  msg = u->packet + packet_len;
1370 
1371  msg[0] = (uint8_t) attr_message_authenticator->attr;
1373  memset(msg + 2, 0, RADIUS_MESSAGE_AUTHENTICATOR_LENGTH);
1374 
1375  packet_len += msg[1];
1376  }
1377 
1378  /*
1379  * Update the packet header based on the new attributes.
1380  */
1381  u->packet[2] = (packet_len >> 8) & 0xff;
1382  u->packet[3] = packet_len & 0xff;
1383  u->packet_len = packet_len;
1384 
1385  /*
1386  * Ensure that we update the Acct-Delay-Time based on the
1387  * time difference between now, and when we originally
1388  * received the request.
1389  */
1391  (fr_pair_find_by_da(&request->request_pairs, NULL, attr_acct_delay_time) != NULL)) {
1392  uint8_t *attr, *end;
1393  uint32_t delay;
1394  fr_time_t now;
1395 
1396  /*
1397  * Change Acct-Delay-Time in the packet, but not
1398  * in the debug output. Oh well. We don't want
1399  * to edit the incoming VPs, and we want to
1400  * update the encoded version of Acct-Delay-Time.
1401  * So we just walk through the packet to find it.
1402  */
1403  end = u->packet + packet_len;
1404 
1405  for (attr = u->packet + RADIUS_HEADER_LENGTH;
1406  attr < end;
1407  attr += attr[1]) {
1408  if (attr[0] != attr_acct_delay_time->attr) continue;
1409  if (attr[1] != 6) continue;
1410 
1411  now = u->retry.updated;
1412 
1413  /*
1414  * Add in the time between when
1415  * we received the packet, and
1416  * when we're sending the packet.
1417  */
1418  memcpy(&delay, attr + 2, 4);
1419  delay = ntohl(delay);
1420  delay += fr_time_delta_to_sec(fr_time_sub(now, u->recv_time));
1421  delay = htonl(delay);
1422  memcpy(attr + 2, &delay, 4);
1423  break;
1424  }
1425 
1426  u->can_retransmit = false;
1427  }
1428 
1429  /*
1430  * Only certain types of packet, and those with a
1431  * message_authenticator need signing.
1432  */
1433  if (message_authenticator) goto sign;
1434  switch (u->code) {
1438  sign:
1439  /*
1440  * Now that we're done mangling the packet, sign it.
1441  */
1442  if (fr_radius_sign(u->packet, NULL, (uint8_t const *) inst->secret,
1443  talloc_array_length(inst->secret) - 1) < 0) {
1444  RERROR("Failed signing packet");
1445  goto error;
1446  }
1447  break;
1448 
1449  default:
1450  break;
1451 
1452  }
1453  return 0;
1454 }
1455 
1456 
1457 /** Revive a connection after "revive_interval"
1458  *
1459  */
1460 static void revive_timeout(UNUSED fr_event_list_t *el, UNUSED fr_time_t now, void *uctx)
1461 {
1462  fr_trunk_connection_t *tconn = talloc_get_type_abort(uctx, fr_trunk_connection_t);
1463  udp_handle_t *h = talloc_get_type_abort(tconn->conn->h, udp_handle_t);
1464 
1465  INFO("%s - Reviving connection %s", h->module_name, h->name);
1467 }
1468 
1469 /** Mark a connection dead after "zombie_interval"
1470  *
1471  */
1472 static void zombie_timeout(fr_event_list_t *el, fr_time_t now, void *uctx)
1473 {
1474  fr_trunk_connection_t *tconn = talloc_get_type_abort(uctx, fr_trunk_connection_t);
1475  udp_handle_t *h = talloc_get_type_abort(tconn->conn->h, udp_handle_t);
1476 
1477  INFO("%s - No replies during 'zombie_period', marking connection %s as dead", h->module_name, h->name);
1478 
1479  /*
1480  * Don't use this connection, and re-queue all of its
1481  * requests onto other connections.
1482  */
1485 
1486  /*
1487  * We do have status checks. Try to reconnect the
1488  * connection immediately. If the status checks pass,
1489  * then the connection will be marked "alive"
1490  */
1491  if (h->inst->parent->status_check) {
1493  return;
1494  }
1495 
1496  /*
1497  * Revive the connection after a time.
1498  */
1499  if (fr_event_timer_at(h, el, &h->zombie_ev,
1500  fr_time_add(now, h->inst->parent->revive_interval), revive_timeout, tconn) < 0) {
1501  ERROR("Failed inserting revive timeout for connection");
1503  }
1504 }
1505 
1506 
1507 /** See if the connection is zombied.
1508  *
1509  * We check for zombie when major events happen:
1510  *
1511  * 1) request hits its final timeout
1512  * 2) request timer hits, and it needs to be retransmitted
1513  * 3) a DUP packet comes in, and the request needs to be retransmitted
1514  * 4) we're sending a packet.
1515  *
1516  * There MIGHT not be retries configured, so we MUST check for zombie
1517  * when any new packet comes in. Similarly, there MIGHT not be new
1518  * packets, but retries are configured, so we have to check there,
1519  * too.
1520  *
1521  * Also, the socket might not be writable for a while. There MIGHT
1522  * be a long time between getting the timer / DUP signal, and the
1523  * request finally being written to the socket. So we need to check
1524  * for zombie at BOTH the timeout and the mux / write function.
1525  *
1526  * @return
1527  * - true if the connection is zombie.
1528  * - false if the connection is not zombie.
1529  */
1531 {
1532  udp_handle_t *h = talloc_get_type_abort(tconn->conn->h, udp_handle_t);
1533 
1534  /*
1535  * We're replicating, and don't care about the health of
1536  * the home server, and this function should not be called.
1537  */
1538  fr_assert(!h->inst->replicate);
1539 
1540  /*
1541  * If we're status checking OR already zombie, don't go to zombie
1542  */
1543  if (h->status_checking || h->zombie_ev) return true;
1544 
1545  if (fr_time_eq(now, fr_time_wrap(0))) now = fr_time();
1546 
1547  /*
1548  * We received a reply since this packet was sent, the connection isn't zombie.
1549  */
1550  if (fr_time_gteq(h->last_reply, last_sent)) return false;
1551 
1552  /*
1553  * If we've seen ANY response in the allowed window, then the connection is still alive.
1554  */
1555  if (h->inst->parent->synchronous && fr_time_gt(last_sent, fr_time_wrap(0)) &&
1556  (fr_time_lt(fr_time_add(last_sent, h->inst->parent->response_window), now))) return false;
1557 
1558  WARN("%s - Entering Zombie state - connection %s", h->module_name, h->name);
1559  if (h->inst->parent->status_check) {
1560  h->status_checking = true;
1561 
1562  /*
1563  * Queue up the status check packet. It will be sent
1564  * when the connection is writable.
1565  */
1566  h->status_u->retry.start = fr_time_wrap(0);
1567  h->status_r->treq = NULL;
1568 
1570  h->status_u, h->status_r, true) != FR_TRUNK_ENQUEUE_OK) {
1572  }
1573  } else {
1575  zombie_timeout, tconn) < 0) {
1576  ERROR("Failed inserting zombie timeout for connection");
1578  }
1579  }
1580 
1581  return true;
1582 }
1583 
1584 /** Handle timeouts when a request is being sent synchronously
1585  *
1586  */
1587 static void request_timeout(fr_event_list_t *el, fr_time_t now, void *uctx)
1588 {
1589  fr_trunk_request_t *treq = talloc_get_type_abort(uctx, fr_trunk_request_t);
1590  udp_request_t *u = talloc_get_type_abort(treq->preq, udp_request_t);
1591  udp_result_t *r = talloc_get_type_abort(treq->rctx, udp_result_t);
1592  fr_trunk_connection_t *tconn = treq->tconn;
1593 
1594  fr_assert(treq->state == FR_TRUNK_REQUEST_STATE_SENT); /* No other states should be timing out */
1595  fr_assert(treq->preq); /* Must still have a protocol request */
1596  fr_assert(u->rr);
1597  fr_assert(tconn);
1598 
1599  r->rcode = RLM_MODULE_FAIL;
1601 
1602  fr_assert(!u->status_check);
1603 
1604  check_for_zombie(el, tconn, now, u->retry.start);
1605 }
1606 
1607 /** Handle retries when a request is being sent asynchronously
1608  *
1609  */
1610 static void request_retry(fr_event_list_t *el, fr_time_t now, void *uctx)
1611 {
1612  fr_trunk_request_t *treq = talloc_get_type_abort(uctx, fr_trunk_request_t);
1613  udp_request_t *u = talloc_get_type_abort(treq->preq, udp_request_t);
1614  udp_result_t *r = talloc_get_type_abort(treq->rctx, udp_result_t);
1615  request_t *request = treq->request;
1616  fr_trunk_connection_t *tconn = treq->tconn;
1617 
1618  fr_assert(treq->state == FR_TRUNK_REQUEST_STATE_SENT); /* No other states should be timing out */
1619  fr_assert(treq->preq); /* Must still have a protocol request */
1620  fr_assert(u->rr);
1621  fr_assert(tconn);
1622 
1623  fr_assert(!u->status_check);
1624 
1625  switch (fr_retry_next(&u->retry, now)) {
1626  /*
1627  * Queue the request for retransmission.
1628  *
1629  * @todo - set up "next" timer here, instead of in
1630  * request_mux() ? That way we can catch the case of
1631  * packets sitting in the queue for extended periods of
1632  * time, and still run the timers.
1633  */
1634  case FR_RETRY_CONTINUE:
1636  return;
1637 
1638  case FR_RETRY_MRD:
1639  REDEBUG("Reached maximum_retransmit_duration (%pVs > %pVs), failing request",
1641  break;
1642 
1643  case FR_RETRY_MRC:
1644  REDEBUG("Reached maximum_retransmit_count (%u > %u), failing request",
1645  u->retry.count, u->retry.config->mrc);
1646  break;
1647  }
1648 
1649  r->rcode = RLM_MODULE_FAIL;
1651 
1652  check_for_zombie(el, tconn, now, u->retry.start);
1653 }
1654 
1655 static void status_check_retry(UNUSED fr_event_list_t *el, fr_time_t now, void *uctx)
1656 {
1657  fr_trunk_request_t *treq = talloc_get_type_abort(uctx, fr_trunk_request_t);
1658  udp_handle_t *h;
1659  udp_request_t *u = talloc_get_type_abort(treq->preq, udp_request_t);
1660  udp_result_t *r = talloc_get_type_abort(treq->rctx, udp_result_t);
1661  request_t *request = treq->request;
1662  fr_trunk_connection_t *tconn = treq->tconn;
1663 
1664  fr_assert(treq->state == FR_TRUNK_REQUEST_STATE_SENT); /* No other states should be timing out */
1665  fr_assert(treq->preq); /* Must still have a protocol request */
1666  fr_assert(u->rr);
1667  fr_assert(tconn);
1668 
1669  h = talloc_get_type_abort(treq->tconn->conn->h, udp_handle_t);
1670 
1671  fr_assert(u->status_check);
1672 
1673  switch (fr_retry_next(&u->retry, now)) {
1674  /*
1675  * Queue the request for retransmission.
1676  *
1677  * @todo - set up "next" timer here, instead of in
1678  * request_mux() ? That way we can catch the case of
1679  * packets sitting in the queue for extended periods of
1680  * time, and still run the timers.
1681  */
1682  case FR_RETRY_CONTINUE:
1684  return;
1685 
1686  case FR_RETRY_MRD:
1687  REDEBUG("Reached maximum_retransmit_duration (%pVs > %pVs), failing request",
1689  break;
1690 
1691  case FR_RETRY_MRC:
1692  REDEBUG("Reached maximum_retransmit_count (%u > %u), failing request",
1693  u->retry.count, u->retry.config->mrc);
1694  break;
1695  }
1696 
1697  r->rcode = RLM_MODULE_FAIL;
1699 
1700  WARN("%s - No response to status check, marking connection as dead - %s", h->module_name, h->name);
1701 
1702  /*
1703  * We're no longer status checking, reconnect the
1704  * connection.
1705  */
1706  h->status_checking = false;
1708 }
1709 
1711  fr_trunk_connection_t *tconn, fr_connection_t *conn, UNUSED void *uctx)
1712 {
1713  udp_handle_t *h = talloc_get_type_abort(conn->h, udp_handle_t);
1714  rlm_radius_udp_t const *inst = h->inst;
1715  int sent;
1716  uint16_t i, queued;
1717  size_t total_len = 0;
1718 
1719  /*
1720  * Encode multiple packets in preparation
1721  * for transmission with sendmmsg.
1722  */
1723  for (i = 0, queued = 0; (i < inst->max_send_coalesce) && (total_len < h->send_buff_actual); i++) {
1724  fr_trunk_request_t *treq;
1725  udp_request_t *u;
1726  request_t *request;
1727 
1728  if (unlikely(fr_trunk_connection_pop_request(&treq, tconn) < 0)) return;
1729 
1730  /*
1731  * No more requests to send
1732  */
1733  if (!treq) break;
1734 
1735  fr_assert((treq->state == FR_TRUNK_REQUEST_STATE_PENDING) ||
1736  (treq->state == FR_TRUNK_REQUEST_STATE_PARTIAL));
1737 
1738  request = treq->request;
1739  u = talloc_get_type_abort(treq->preq, udp_request_t);
1740 
1741  /*
1742  * Start retransmissions from when the socket is writable.
1743  */
1744  if (fr_time_eq(u->retry.start, fr_time_wrap(0))) {
1745  fr_retry_init(&u->retry, fr_time(), &h->inst->parent->retry[u->code]);
1748  }
1749 
1750  /*
1751  * No previous packet, OR can't retransmit the
1752  * existing one. Oh well.
1753  *
1754  * Note that if we can't retransmit the previous
1755  * packet, then u->rr MUST already have been
1756  * deleted in the request_cancel() function
1757  * or request_release_conn() function when
1758  * the REQUEUE signal was received.
1759  */
1760  if (!u->packet || !u->can_retransmit) {
1761  fr_assert(!u->rr);
1762 
1763  if (unlikely(radius_track_entry_reserve(&u->rr, treq, h->tt, request, u->code, treq) < 0)) {
1764 #ifndef NDEBUG
1765  radius_track_state_log(&default_log, L_ERR, __FILE__, __LINE__,
1767 #endif
1768  fr_assert_fail("Tracking entry allocation failed: %s", fr_strerror());
1770  continue;
1771  }
1772  u->id = u->rr->id;
1773 
1774  RDEBUG("Sending %s ID %d length %ld over connection %s",
1775  fr_radius_packet_name[u->code], u->id, u->packet_len, h->name);
1776 
1777  if (encode(h->inst, request, u, u->id) < 0) {
1778  /*
1779  * Need to do this because request_conn_release
1780  * may not be called.
1781  */
1782  udp_request_reset(u);
1783  if (u->ev) (void) fr_event_timer_delete(&u->ev);
1785  continue;
1786  }
1787  RHEXDUMP3(u->packet, u->packet_len, "Encoded packet");
1788 
1789  /*
1790  * Remember the authentication vector, which now has the
1791  * packet signature.
1792  */
1794  } else {
1795  RDEBUG("Retransmitting %s ID %d length %ld over connection %s",
1796  fr_radius_packet_name[u->code], u->id, u->packet_len, h->name);
1797  }
1798 
1799  log_request_pair_list(L_DBG_LVL_2, request, NULL, &request->request_pairs, NULL);
1800  if (!fr_pair_list_empty(&u->extra)) log_request_pair_list(L_DBG_LVL_2, request, NULL, &u->extra, NULL);
1801 
1802  /*
1803  * Record pointers to the buffer we'll be writing
1804  * We store the treq so we can place it back in
1805  * the pending state if the sendmmsg call fails.
1806  */
1807  h->coalesced[queued].treq = treq;
1808  h->coalesced[queued].out.iov_base = u->packet;
1809  h->coalesced[queued].out.iov_len = u->packet_len;
1810 
1811  /*
1812  * Record how much data we have in total.
1813  *
1814  * Try not to exceed the SO_SNDBUF value of the
1815  * socket as we potentially just waste CPU
1816  * time re-encoding the packets.
1817  */
1818  total_len += u->packet_len;
1819 
1820  /*
1821  * Tell the trunk API that this request is now in
1822  * the "sent" state. And we don't want to see
1823  * this request again. The request hasn't actually
1824  * been sent, but it's the only way to get at the
1825  * next entry in the heap.
1826  */
1828  queued++;
1829  }
1830  if (queued == 0) return; /* No work */
1831 
1832  /*
1833  * Verify nothing accidentally freed the connection handle
1834  */
1835  (void)talloc_get_type_abort(h, udp_handle_t);
1836 
1837  /*
1838  * Send the coalesced datagrams
1839  */
1840  sent = sendmmsg(h->fd, h->mmsgvec, queued, 0);
1841  if (sent < 0) { /* Error means no messages were sent */
1842  sent = 0;
1843 
1844  /*
1845  * Temporary conditions
1846  */
1847  switch (errno) {
1848 #if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN)
1849  case EWOULDBLOCK: /* No outbound packet buffers, maybe? */
1850 #endif
1851  case EAGAIN: /* No outbound packet buffers, maybe? */
1852  case EINTR: /* Interrupted by signal */
1853  case ENOBUFS: /* No outbound packet buffers, maybe? */
1854  case ENOMEM: /* malloc failure in kernel? */
1855  WARN("%s - Failed sending data over connection %s: %s",
1856  h->module_name, h->name, fr_syserror(errno));
1857  break;
1858 
1859  /*
1860  * Fatal, request specific conditions
1861  *
1862  * sendmmsg will only return an error condition if the
1863  * first packet being sent errors.
1864  *
1865  * When we get request specific errors, we need to fail
1866  * the first request in the set, and move the rest of
1867  * the packets back to the pending state.
1868  */
1869  case EMSGSIZE: /* Packet size exceeds max size allowed on socket */
1870  ERROR("%s - Failed sending data over connection %s: %s",
1871  h->module_name, h->name, fr_syserror(errno));
1873  sent = 1;
1874  break;
1875 
1876  /*
1877  * Will re-queue any 'sent' requests, so we don't
1878  * have to do any cleanup.
1879  */
1880  default:
1881  ERROR("%s - Failed sending data over connection %s: %s",
1882  h->module_name, h->name, fr_syserror(errno));
1884  return;
1885  }
1886  }
1887 
1888  /*
1889  * For all messages that were actually sent by sendmmsg
1890  * start the request timer.
1891  */
1892  for (i = 0; i < sent; i++) {
1893  fr_trunk_request_t *treq = h->coalesced[i].treq;
1894  udp_request_t *u;
1895  request_t *request;
1896  char const *action;
1897 
1898  /*
1899  * It's UDP so there should never be partial writes
1900  */
1901  fr_assert((size_t)h->mmsgvec[i].msg_len == h->mmsgvec[i].msg_hdr.msg_iov->iov_len);
1902 
1903  fr_assert(treq->state == FR_TRUNK_REQUEST_STATE_SENT);
1904 
1905  request = treq->request;
1906  u = talloc_get_type_abort(treq->preq, udp_request_t);
1907 
1908  /*
1909  * Tell the admin what's going on
1910  */
1911  if (u->retry.count == 1) {
1912  action = inst->parent->originate ? "Originated" : "Proxied";
1913  h->last_sent = u->retry.start;
1914  if (fr_time_lteq(h->first_sent, h->last_idle)) h->first_sent = h->last_sent;
1915 
1916  } else {
1917  action = "Retransmitted";
1918  }
1919 
1920  if (u->status_check) {
1921  RDEBUG("%s status check. Expecting response within %pVs", action,
1922  fr_box_time_delta(u->retry.rt));
1923 
1924  if (fr_event_timer_at(u, el, &u->ev, u->retry.next, status_check_retry, treq) < 0) {
1925  RERROR("Failed inserting retransmit timeout for connection");
1927  continue;
1928  }
1929 
1930  } else if (!inst->parent->synchronous) {
1931  RDEBUG("%s request. Expecting response within %pVs", action,
1932  fr_box_time_delta(u->retry.rt));
1933 
1934  if (fr_event_timer_at(u, el, &u->ev, u->retry.next, request_retry, treq) < 0) {
1935  RERROR("Failed inserting retransmit timeout for connection");
1937  continue;
1938  }
1939 
1940  } else if (u->retry.count == 1) {
1941  if (fr_event_timer_at(u, el, &u->ev,
1943  request_timeout, treq) < 0) {
1944  RERROR("Failed inserting timeout for connection");
1946  continue;
1947  }
1948 
1949  /*
1950  * If the packet doesn't get a response,
1951  * then udp_request_free() will notice, and run conn_zombie()
1952  */
1953  RDEBUG("%s request. Relying on NAS to perform more retransmissions", action);
1954  }
1955  }
1956 
1957  /*
1958  * Requests that weren't sent get re-enqueued
1959  *
1960  * The cancel logic runs as per-normal and cleans up
1961  * the request ready for sending again...
1962  */
1963  for (i = sent; i < queued; i++) fr_trunk_request_requeue(h->coalesced[i].treq);
1964 }
1965 
1967  fr_trunk_connection_t *tconn, fr_connection_t *conn, UNUSED void *uctx)
1968 {
1969  udp_handle_t *h = talloc_get_type_abort(conn->h, udp_handle_t);
1970  rlm_radius_udp_t const *inst = h->inst;
1971 
1972  uint16_t i = 0, queued;
1973  int sent;
1974  size_t total_len = 0;
1975 
1976  for (i = 0, queued = 0; (i < inst->max_send_coalesce) && (total_len < h->send_buff_actual); i++) {
1977  fr_trunk_request_t *treq;
1978  udp_request_t *u;
1979  request_t *request;
1980 
1981  if (unlikely(fr_trunk_connection_pop_request(&treq, tconn) < 0)) return;
1982 
1983  /*
1984  * No more requests to send
1985  */
1986  if (!treq) break;
1987 
1988  fr_assert((treq->state == FR_TRUNK_REQUEST_STATE_PENDING) ||
1989  (treq->state == FR_TRUNK_REQUEST_STATE_PARTIAL));
1990 
1991  request = treq->request;
1992  u = talloc_get_type_abort(treq->preq, udp_request_t);
1993 
1994  if (!u->packet) {
1995  u->id = h->last_id++;
1996 
1997  if (encode(h->inst, request, u, u->id) < 0) {
1999  continue;
2000  }
2001  }
2002 
2003  RDEBUG("Sending %s ID %d length %ld over connection %s",
2004  fr_radius_packet_name[u->code], u->id, u->packet_len, h->name);
2005  RHEXDUMP3(u->packet, u->packet_len, "Encoded packet");
2006 
2007  h->coalesced[queued].treq = treq;
2008  h->coalesced[queued].out.iov_base = u->packet;
2009  h->coalesced[queued].out.iov_len = u->packet_len;
2010 
2011  /*
2012  * Record how much data we have in total.
2013  *
2014  * Try not to exceed the SO_SNDBUF value of the
2015  * socket as we potentially just waste CPU
2016  * time re-encoding the packets.
2017  */
2018  total_len += u->packet_len;
2019 
2021  queued++;
2022  }
2023  if (queued == 0) return; /* No work */
2024 
2025  /*
2026  * Verify nothing accidentally freed the connection handle
2027  */
2028  (void)talloc_get_type_abort(h, udp_handle_t);
2029 
2030  sent = sendmmsg(h->fd, h->mmsgvec, queued, 0);
2031  if (sent < 0) { /* Error means no messages were sent */
2032  sent = 0;
2033 
2034  /*
2035  * Temporary conditions
2036  */
2037  switch (errno) {
2038 #if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN)
2039  case EWOULDBLOCK: /* No outbound packet buffers, maybe? */
2040 #endif
2041  case EAGAIN: /* No outbound packet buffers, maybe? */
2042  case EINTR: /* Interrupted by signal */
2043  case ENOBUFS: /* No outbound packet buffers, maybe? */
2044  case ENOMEM: /* malloc failure in kernel? */
2045  WARN("%s - Failed sending data over connection %s: %s",
2046  h->module_name, h->name, fr_syserror(errno));
2047  break;
2048 
2049  /*
2050  * Fatal, request specific conditions
2051  *
2052  * sendmmsg will only return an error condition if the
2053  * first packet being sent errors.
2054  *
2055  * When we get request specific errors, we need to fail
2056  * the first request in the set, and move the rest of
2057  * the packets back to the pending state.
2058  */
2059  case EMSGSIZE: /* Packet size exceeds max size allowed on socket */
2060  ERROR("%s - Failed sending data over connection %s: %s",
2061  h->module_name, h->name, fr_syserror(errno));
2063  sent = 1;
2064  break;
2065 
2066  /*
2067  * Will re-queue any 'sent' requests, so we don't
2068  * have to do any cleanup.
2069  */
2070  default:
2071  ERROR("%s - Failed sending data over connection %s: %s",
2072  h->module_name, h->name, fr_syserror(errno));
2074  return;
2075  }
2076  }
2077 
2078  for (i = 0; i < sent; i++) {
2079  fr_trunk_request_t *treq = h->coalesced[i].treq;
2080  udp_result_t *r = talloc_get_type_abort(treq->rctx, udp_result_t);
2081 
2082  /*
2083  * It's UDP so there should never be partial writes
2084  */
2085  fr_assert((size_t)h->mmsgvec[i].msg_len == h->mmsgvec[i].msg_hdr.msg_iov->iov_len);
2086 
2087  r->rcode = RLM_MODULE_OK;
2089  }
2090 
2091  for (i = sent; i < queued; i++) fr_trunk_request_requeue(h->coalesced[i].treq);
2092 }
2093 
2094 /** Deal with Protocol-Error replies, and possible negotiation
2095  *
2096  */
2098 {
2099  bool error_601 = false;
2100  uint32_t response_length = 0;
2101  uint8_t const *attr, *end;
2102 
2103  end = h->buffer + fr_nbo_to_uint16(h->buffer + 2);
2104 
2105  for (attr = h->buffer + RADIUS_HEADER_LENGTH;
2106  attr < end;
2107  attr += attr[1]) {
2108  /*
2109  * Error-Cause = Response-Too-Big
2110  */
2111  if ((attr[0] == attr_error_cause->attr) && (attr[1] == 6)) {
2112  uint32_t error;
2113 
2114  memcpy(&error, attr + 2, 4);
2115  error = ntohl(error);
2116  if (error == 601) error_601 = true;
2117  continue;
2118  }
2119 
2120  /*
2121  * The other end wants us to increase our Response-Length
2122  */
2123  if ((attr[0] == attr_response_length->attr) && (attr[1] == 6)) {
2124  memcpy(&response_length, attr + 2, 4);
2125  continue;
2126  }
2127 
2128  /*
2129  * Protocol-Error packets MUST contain an
2130  * Original-Packet-Code attribute.
2131  *
2132  * The attribute containing the
2133  * Original-Packet-Code is an extended
2134  * attribute.
2135  */
2136  if (attr[0] != attr_extended_attribute_1->attr) continue;
2137 
2138  /*
2139  * ATTR + LEN + EXT-Attr + uint32
2140  */
2141  if (attr[1] != 7) continue;
2142 
2143  /*
2144  * See if there's an Original-Packet-Code.
2145  */
2146  if (attr[2] != (uint8_t)attr_original_packet_code->attr) continue;
2147 
2148  /*
2149  * Has to be an 8-bit number.
2150  */
2151  if ((attr[3] != 0) ||
2152  (attr[4] != 0) ||
2153  (attr[5] != 0)) {
2154  if (r) r->rcode = RLM_MODULE_FAIL;
2155  return;
2156  }
2157 
2158  /*
2159  * The value has to match. We don't
2160  * currently multiplex different codes
2161  * with the same IDs on connections. So
2162  * this check is just for RFC compliance,
2163  * and for sanity.
2164  */
2165  if (attr[6] != u->code) {
2166  if (r) r->rcode = RLM_MODULE_FAIL;
2167  return;
2168  }
2169  }
2170 
2171  /*
2172  * Error-Cause = Response-Too-Big
2173  *
2174  * The other end says it needs more room to send it's response
2175  *
2176  * Limit it to reasonable values.
2177  */
2178  if (error_601 && response_length && (response_length > h->buflen)) {
2179  if (response_length < 4096) response_length = 4096;
2180  if (response_length > 65535) response_length = 65535;
2181 
2182  DEBUG("%s - Increasing buffer size to %u for connection %s", h->module_name, response_length, h->name);
2183 
2184  /*
2185  * Make sure to copy the packet over!
2186  */
2187  attr = h->buffer;
2188  h->buflen = response_length;
2189  MEM(h->buffer = talloc_array(h, uint8_t, h->buflen));
2190 
2191  memcpy(h->buffer, attr, end - attr);
2192  }
2193 
2194  /*
2195  * fail - something went wrong internally, or with the connection.
2196  * invalid - wrong response to packet
2197  * handled - best remaining alternative :(
2198  *
2199  * i.e. if the response is NOT accept, reject, whatever,
2200  * then we shouldn't allow the caller to do any more
2201  * processing of this packet. There was a protocol
2202  * error, and the response is valid, but not useful for
2203  * anything.
2204  */
2205  if (r) r->rcode = RLM_MODULE_HANDLED;
2206 }
2207 
2208 
2209 /** Handle retries for a status check
2210  *
2211  */
2213 {
2214  fr_trunk_connection_t *tconn = talloc_get_type_abort(uctx, fr_trunk_connection_t);
2215  udp_handle_t *h = talloc_get_type_abort(tconn->conn->h, udp_handle_t);
2216 
2218  h->status_u, h->status_r, true) != FR_TRUNK_ENQUEUE_OK) {
2220  }
2221 }
2222 
2223 
2224 /** Deal with replies replies to status checks and possible negotiation
2225  *
2226  */
2228 {
2229  udp_handle_t *h = talloc_get_type_abort(treq->tconn->conn->h, udp_handle_t);
2230  rlm_radius_t const *inst = h->inst->parent;
2231  udp_request_t *u = talloc_get_type_abort(treq->preq, udp_request_t);
2232  udp_result_t *r = talloc_get_type_abort(treq->rctx, udp_result_t);
2233 
2234  fr_assert(treq->preq == h->status_u);
2235  fr_assert(treq->rctx == h->status_r);
2236 
2237  r->treq = NULL;
2238 
2239  /*
2240  * @todo - do other negotiation and signaling.
2241  */
2242  if (h->buffer[0] == FR_RADIUS_CODE_PROTOCOL_ERROR) protocol_error_reply(u, NULL, h);
2243 
2244  if (u->num_replies < inst->num_answers_to_alive) {
2245  DEBUG("Received %d / %u replies for status check, on connection - %s",
2246  u->num_replies, inst->num_answers_to_alive, h->name);
2247  DEBUG("Next status check packet will be in %pVs", fr_box_time_delta(fr_time_sub(u->retry.next, now)));
2248 
2249  /*
2250  * If we're retransmitting, leave the ID,
2251  * packet and associated resources alone.
2252  *
2253  * Otherwise free resources.
2254  */
2255  if (!u->can_retransmit) udp_request_reset(u);
2256 
2257  /*
2258  * Set the timer for the next retransmit.
2259  */
2260  if (fr_event_timer_at(h, h->thread->el, &u->ev, u->retry.next, status_check_next, treq->tconn) < 0) {
2262  }
2263  return;
2264  }
2265 
2266  DEBUG("Received enough replies to status check, marking connection as active - %s", h->name);
2267 
2268  /*
2269  * Set the "last idle" time to now, so that we don't
2270  * restart zombie_period until sufficient time has
2271  * passed.
2272  */
2273  h->last_idle = fr_time();
2274 
2275  /*
2276  * Reset retry interval and retransmission counters
2277  * also frees u->ev.
2278  */
2279  status_check_reset(h, u);
2280  fr_trunk_connection_signal_active(treq->tconn);
2281 }
2282 
2284 {
2285  udp_handle_t *h = talloc_get_type_abort(conn->h, udp_handle_t);
2286 
2287  DEBUG3("%s - Reading data for connection %s", h->module_name, h->name);
2288 
2289  while (true) {
2290  ssize_t slen;
2291 
2292  fr_trunk_request_t *treq;
2293  request_t *request;
2294  udp_request_t *u;
2295  udp_result_t *r;
2297  decode_fail_t reason;
2298  uint8_t code = 0;
2299  fr_pair_list_t reply;
2300 
2301  fr_time_t now;
2302 
2303  fr_pair_list_init(&reply);
2304  /*
2305  * Drain the socket of all packets. If we're busy, this
2306  * saves a round through the event loop. If we're not
2307  * busy, a few extra system calls don't matter.
2308  */
2309  slen = read(h->fd, h->buffer, h->buflen);
2310  if (slen == 0) return;
2311 
2312  if (slen < 0) {
2313  if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) return;
2314 
2315  ERROR("%s - Failed reading response from socket: %s",
2316  h->module_name, fr_syserror(errno));
2318  return;
2319  }
2320 
2321  if (slen < RADIUS_HEADER_LENGTH) {
2322  ERROR("%s - Packet too short, expected at least %zu bytes got %zd bytes",
2323  h->module_name, (size_t)RADIUS_HEADER_LENGTH, slen);
2324  continue;
2325  }
2326 
2327  /*
2328  * Note that we don't care about packet codes. All
2329  * packet codes share the same ID space.
2330  */
2331  rr = radius_track_entry_find(h->tt, h->buffer[1], NULL);
2332  if (!rr) {
2333  WARN("%s - Ignoring reply with ID %i that arrived too late",
2334  h->module_name, h->buffer[1]);
2335  continue;
2336  }
2337 
2338  treq = talloc_get_type_abort(rr->uctx, fr_trunk_request_t);
2339  request = treq->request;
2340  fr_assert(request != NULL);
2341  u = talloc_get_type_abort(treq->preq, udp_request_t);
2342  r = talloc_get_type_abort(treq->rctx, udp_result_t);
2343 
2344  /*
2345  * Validate and decode the incoming packet
2346  */
2347 
2348  if (!check(h, &slen)) {
2349  RWARN("Ignoring malformed packet");
2350  continue;
2351  }
2352 
2353  reason = decode(request->reply_ctx, &reply, &code, h, request, u, rr->vector, h->buffer, (size_t)slen);
2354  if (reason != DECODE_FAIL_NONE) continue;
2355 
2356  /*
2357  * Only valid packets are processed
2358  * Otherwise an attacker could perform
2359  * a DoS attack against the proxying servers
2360  * by sending fake responses for upstream
2361  * servers.
2362  */
2363  h->last_reply = now = fr_time();
2364 
2365  /*
2366  * Status-Server can have any reply code, we don't care
2367  * what it is. So long as it's signed properly, we
2368  * accept it. This flexibility is because we don't
2369  * expose Status-Server to the admins. It's only used by
2370  * this module for internal signalling.
2371  */
2372  if (u == h->status_u) {
2373  fr_pair_list_free(&reply); /* Probably want to pass this to status_check_reply? */
2374  status_check_reply(treq, now);
2376  continue;
2377  }
2378 
2379  /*
2380  * Handle any state changes, etc. needed by receiving a
2381  * Protocol-Error reply packet.
2382  *
2383  * Protocol-Error is permitted as a reply to any
2384  * packet.
2385  */
2386  switch (code) {
2388  protocol_error_reply(u, r, h);
2389  break;
2390 
2391  default:
2392  break;
2393  }
2394 
2395  /*
2396  * Mark up the request as being an Access-Challenge, if
2397  * required.
2398  *
2399  * We don't do this for other packet types, because the
2400  * ok/fail nature of the module return code will
2401  * automatically result in it the parent request
2402  * returning an ok/fail packet code.
2403  */
2405  fr_pair_t *vp;
2406 
2407  vp = fr_pair_find_by_da(&request->reply_pairs, NULL, attr_packet_type);
2408  if (!vp) {
2409  MEM(vp = fr_pair_afrom_da(request->reply_ctx, attr_packet_type));
2410  vp->vp_uint32 = FR_RADIUS_CODE_ACCESS_CHALLENGE;
2411  fr_pair_append(&request->reply_pairs, vp);
2412  }
2413  }
2414 
2415  /*
2416  * Delete Proxy-State attributes from the reply.
2417  */
2419 
2420  /*
2421  * If the reply has Message-Authenticator, delete
2422  * it from the proxy reply so that it isn't
2423  * copied over to our reply. But also create a
2424  * reply.Message-Authenticator attribute, so that
2425  * it ends up in our reply.
2426  */
2427  if (fr_pair_find_by_da(&reply, NULL, attr_message_authenticator)) {
2428  fr_pair_t *vp;
2429 
2431 
2432  MEM(vp = fr_pair_afrom_da(request->reply_ctx, attr_message_authenticator));
2433  (void) fr_pair_value_memdup(vp, (uint8_t const *) "", 1, false);
2434  fr_pair_append(&request->reply_pairs, vp);
2435  }
2436 
2437  treq->request->reply->code = code;
2438  r->rcode = radius_code_to_rcode[code];
2439  fr_pair_list_append(&request->reply_pairs, &reply);
2441  }
2442 }
2443 
2444 /** Remove the request from any tracking structures
2445  *
2446  * Frees encoded packets if the request is being moved to a new connection
2447  */
2448 static void request_cancel(UNUSED fr_connection_t *conn, void *preq_to_reset,
2449  fr_trunk_cancel_reason_t reason, UNUSED void *uctx)
2450 {
2451  udp_request_t *u = talloc_get_type_abort(preq_to_reset, udp_request_t);
2452 
2453  /*
2454  * Request has been requeued on the same
2455  * connection due to timeout or DUP signal. We
2456  * keep the same packet to avoid re-encoding it.
2457  */
2458  if (reason == FR_TRUNK_CANCEL_REASON_REQUEUE) {
2459  /*
2460  * Delete the request_timeout
2461  *
2462  * Note: There might not be a request timeout
2463  * set in the case where the request was
2464  * queued for sendmmsg but never actually
2465  * sent.
2466  */
2467  if (u->ev) (void) fr_event_timer_delete(&u->ev);
2468  if (!u->can_retransmit) udp_request_reset(u);
2469  }
2470 
2471  /*
2472  * Other cancellations are dealt with by
2473  * request_conn_release as the request is removed
2474  * from the trunk.
2475  */
2476 }
2477 
2478 /** Clear out anything associated with the handle from the request
2479  *
2480  */
2481 static void request_conn_release(fr_connection_t *conn, void *preq_to_reset, UNUSED void *uctx)
2482 {
2483  udp_request_t *u = talloc_get_type_abort(preq_to_reset, udp_request_t);
2484  udp_handle_t *h = talloc_get_type_abort(conn->h, udp_handle_t);
2485 
2486  if (u->ev) (void)fr_event_timer_delete(&u->ev);
2487  if (u->packet) udp_request_reset(u);
2488 
2489  u->num_replies = 0;
2490 
2491  /*
2492  * If there are no outstanding tracking entries
2493  * allocated then the connection is "idle".
2494  */
2495  if (!h->tt || (h->tt->num_requests == 0)) h->last_idle = fr_time();
2496 }
2497 
2498 /** Clear out anything associated with the handle from the request
2499  *
2500  */
2501 static void request_conn_release_replicate(UNUSED fr_connection_t *conn, void *preq_to_reset, UNUSED void *uctx)
2502 {
2503  udp_request_t *u = talloc_get_type_abort(preq_to_reset, udp_request_t);
2504 
2505  fr_assert(!u->ev);
2506 
2507  if (u->packet) udp_request_reset(u);
2508 }
2509 
2510 /** Write out a canned failure
2511  *
2512  */
2513 static void request_fail(request_t *request, void *preq, void *rctx,
2514  NDEBUG_UNUSED fr_trunk_request_state_t state, UNUSED void *uctx)
2515 {
2516  udp_result_t *r = talloc_get_type_abort(rctx, udp_result_t);
2517  udp_request_t *u = talloc_get_type_abort(preq, udp_request_t);
2518 
2519  fr_assert(!u->rr && !u->packet && fr_pair_list_empty(&u->extra) && !u->ev); /* Dealt with by request_conn_release */
2520 
2522 
2523  if (u->status_check) return;
2524 
2525  r->rcode = RLM_MODULE_FAIL;
2526  r->treq = NULL;
2527 
2529 }
2530 
2531 /** Response has already been written to the rctx at this point
2532  *
2533  */
2534 static void request_complete(request_t *request, void *preq, void *rctx, UNUSED void *uctx)
2535 {
2536  udp_result_t *r = talloc_get_type_abort(rctx, udp_result_t);
2537  udp_request_t *u = talloc_get_type_abort(preq, udp_request_t);
2538 
2539  fr_assert(!u->rr && !u->packet && fr_pair_list_empty(&u->extra) && !u->ev); /* Dealt with by request_conn_release */
2540 
2541  if (u->status_check) return;
2542 
2543  r->treq = NULL;
2544 
2546 }
2547 
2548 /** Explicitly free resources associated with the protocol request
2549  *
2550  */
2551 static void request_free(UNUSED request_t *request, void *preq_to_free, UNUSED void *uctx)
2552 {
2553  udp_request_t *u = talloc_get_type_abort(preq_to_free, udp_request_t);
2554 
2555  fr_assert(!u->rr && !u->packet && fr_pair_list_empty(&u->extra) && !u->ev); /* Dealt with by request_conn_release */
2556 
2557  /*
2558  * Don't free status check requests.
2559  */
2560  if (u->status_check) return;
2561 
2562  talloc_free(u);
2563 }
2564 
2565 /** Resume execution of the request, returning the rcode set during trunk execution
2566  *
2567  */
2568 static unlang_action_t mod_resume(rlm_rcode_t *p_result, module_ctx_t const *mctx, UNUSED request_t *request)
2569 {
2570  udp_result_t *r = talloc_get_type_abort(mctx->rctx, udp_result_t);
2571  rlm_rcode_t rcode = r->rcode;
2572 
2573  talloc_free(r);
2574 
2575  RETURN_MODULE_RCODE(rcode);
2576 }
2577 
2578 static void mod_signal(module_ctx_t const *mctx, UNUSED request_t *request, fr_signal_t action)
2579 {
2580  udp_thread_t *t = talloc_get_type_abort(mctx->thread, udp_thread_t);
2581  udp_result_t *r = talloc_get_type_abort(mctx->rctx, udp_result_t);
2582 
2583  /*
2584  * If we don't have a treq associated with the
2585  * rctx it's likely because the request was
2586  * scheduled, but hasn't yet been resumed, and
2587  * has received a signal, OR has been resumed
2588  * and immediately cancelled as the event loop
2589  * is exiting, in which case
2590  * unlang_request_is_scheduled will return false
2591  * (don't use it).
2592  */
2593  if (!r->treq) {
2594  talloc_free(r);
2595  return;
2596  }
2597 
2598  switch (action) {
2599  /*
2600  * The request is being cancelled, tell the
2601  * trunk so it can clean up the treq.
2602  */
2603  case FR_SIGNAL_CANCEL:
2605  r->treq = NULL;
2606  talloc_free(r); /* Should be freed soon anyway, but better to be explicit */
2607  return;
2608 
2609  /*
2610  * Requeue the request on the same connection
2611  * causing a "retransmission" if the request
2612  * has already been sent out.
2613  */
2614  case FR_SIGNAL_DUP:
2615  /*
2616  * If we're not synchronous, then rely on
2617  * request_retry() to do the retransmissions.
2618  */
2619  if (!t->inst->parent->synchronous) return;
2620 
2621  /*
2622  * We are synchronous, retransmit the current
2623  * request on the same connection.
2624  *
2625  * If it's zombie, we still resend it. If the
2626  * connection is dead, then a callback will move
2627  * this request to a new connection.
2628  */
2630  return;
2631 
2632  default:
2633  return;
2634  }
2635 }
2636 
2637 #ifndef NDEBUG
2638 /** Free a udp_result_t
2639  *
2640  * Allows us to set break points for debugging.
2641  */
2643 {
2644  fr_trunk_request_t *treq;
2645  udp_request_t *u;
2646 
2647  if (!r->treq) return 0;
2648 
2649  treq = talloc_get_type_abort(r->treq, fr_trunk_request_t);
2650  u = talloc_get_type_abort(treq->preq, udp_request_t);
2651 
2652  fr_assert_msg(!u->ev, "udp_result_t freed with active timer");
2653 
2654  return 0;
2655 }
2656 #endif
2657 
2658 /** Free a udp_request_t
2659  */
2661 {
2662  if (u->ev) (void) fr_event_timer_delete(&u->ev);
2663 
2664  fr_assert(u->rr == NULL);
2665 
2666  return 0;
2667 }
2668 
2669 static unlang_action_t mod_enqueue(rlm_rcode_t *p_result, void **rctx_out, void *instance, void *thread, request_t *request)
2670 {
2671  rlm_radius_udp_t *inst = talloc_get_type_abort(instance, rlm_radius_udp_t);
2672  udp_thread_t *t = talloc_get_type_abort(thread, udp_thread_t);
2673  udp_result_t *r;
2674  udp_request_t *u;
2675  fr_trunk_request_t *treq;
2676 
2677  fr_assert(request->packet->code > 0);
2678  fr_assert(request->packet->code < FR_RADIUS_CODE_MAX);
2679 
2680  if (request->packet->code == FR_RADIUS_CODE_STATUS_SERVER) {
2681  RWDEBUG("Status-Server is reserved for internal use, and cannot be sent manually.");
2683  }
2684 
2685  treq = fr_trunk_request_alloc(t->trunk, request);
2686  if (!treq) RETURN_MODULE_FAIL;
2687 
2688  MEM(r = talloc_zero(request, udp_result_t));
2689 #ifndef NDEBUG
2690  talloc_set_destructor(r, _udp_result_free);
2691 #endif
2692 
2693  /*
2694  * Can't use compound literal - const issues.
2695  */
2696  MEM(u = talloc_zero(treq, udp_request_t));
2697  u->code = request->packet->code;
2698  u->synchronous = inst->parent->synchronous;
2699  u->priority = request->async->priority;
2700  u->recv_time = request->async->recv_time;
2701  fr_pair_list_init(&u->extra);
2702 
2703  r->rcode = RLM_MODULE_FAIL;
2704 
2705  /*
2706  * Make sure that we print out the actual encoded value
2707  * of the Message-Authenticator attribute. If the caller
2708  * asked for one, delete theirs (which has a bad value),
2709  * and remember to add one manually when we encode the
2710  * packet. This is the only editing we do on the input
2711  * request.
2712  *
2713  * @todo - don't edit the input packet!
2714  */
2715  if (fr_pair_find_by_da(&request->request_pairs, NULL, attr_message_authenticator)) {
2716  u->require_ma = true;
2718  }
2719 
2720  switch(fr_trunk_request_enqueue(&treq, t->trunk, request, u, r)) {
2721  case FR_TRUNK_ENQUEUE_OK:
2723  break;
2724 
2726  REDEBUG("Unable to queue packet - connections at maximum capacity");
2727  fail:
2728  fr_assert(!u->rr && !u->packet); /* Should not have been fed to the muxer */
2729  fr_trunk_request_free(&treq); /* Return to the free list */
2730  talloc_free(r);
2732 
2734  REDEBUG("All destinations are down - cannot send packet");
2735  goto fail;
2736 
2737  case FR_TRUNK_ENQUEUE_FAIL:
2738  REDEBUG("Unable to queue packet");
2739  goto fail;
2740  }
2741 
2742  r->treq = treq; /* Remember for signalling purposes */
2743 
2744  talloc_set_destructor(u, _udp_request_free);
2745 
2746  *rctx_out = r;
2747 
2748  return UNLANG_ACTION_YIELD;
2749 }
2750 
2751 /** Instantiate thread data for the submodule.
2752  *
2753  */
2755 {
2756  rlm_radius_udp_t *inst = talloc_get_type_abort(mctx->inst->data, rlm_radius_udp_t);
2757  udp_thread_t *thread = talloc_get_type_abort(mctx->thread, udp_thread_t);
2758 
2759  static fr_trunk_io_funcs_t io_funcs = {
2761  .connection_notify = thread_conn_notify,
2762  .request_prioritise = request_prioritise,
2763  .request_mux = request_mux,
2764  .request_demux = request_demux,
2765  .request_conn_release = request_conn_release,
2766  .request_complete = request_complete,
2767  .request_fail = request_fail,
2768  .request_cancel = request_cancel,
2769  .request_free = request_free
2770  };
2771 
2772  static fr_trunk_io_funcs_t io_funcs_replicate = {
2774  .connection_notify = thread_conn_notify_replicate,
2775  .request_prioritise = request_prioritise,
2776  .request_mux = request_mux_replicate,
2777  .request_conn_release = request_conn_release_replicate,
2778  .request_complete = request_complete,
2779  .request_fail = request_fail,
2780  .request_free = request_free
2781  };
2782 
2783  inst->trunk_conf = &inst->parent->trunk_conf;
2784 
2785  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 */
2786  inst->trunk_conf->req_pool_size = sizeof(udp_request_t) + inst->max_packet_size + sizeof(radius_track_entry_t ***) + sizeof(fr_pair_t) + 20;
2787 
2788  thread->el = mctx->el;
2789  thread->inst = inst;
2790  thread->trunk = fr_trunk_alloc(thread, mctx->el, inst->replicate ? &io_funcs_replicate : &io_funcs,
2791  inst->trunk_conf, inst->parent->name, thread, false);
2792  if (!thread->trunk) return -1;
2793 
2794  return 0;
2795 }
2796 
2797 static int mod_instantiate(module_inst_ctx_t const *mctx)
2798 {
2799  rlm_radius_t *parent = talloc_get_type_abort(mctx->inst->parent->data, rlm_radius_t);
2800  rlm_radius_udp_t *inst = talloc_get_type_abort(mctx->inst->data, rlm_radius_udp_t);
2801  CONF_SECTION *conf = mctx->inst->conf;
2802 
2803  if (!parent) {
2804  ERROR("IO module cannot be instantiated directly");
2805  return -1;
2806  }
2807 
2808  inst->parent = parent;
2809  inst->replicate = parent->replicate;
2810 
2811  /*
2812  * Always need at least one mmsgvec
2813  */
2814  if (inst->max_send_coalesce == 0) inst->max_send_coalesce = 1;
2815 
2816  /*
2817  * Ensure that we have a destination address.
2818  */
2819  if (inst->dst_ipaddr.af == AF_UNSPEC) {
2820  cf_log_err(conf, "A value must be given for 'ipaddr'");
2821  return -1;
2822  }
2823 
2824  /*
2825  * If src_ipaddr isn't set, make sure it's INADDR_ANY, of
2826  * the same address family as dst_ipaddr.
2827  */
2828  if (inst->src_ipaddr.af == AF_UNSPEC) {
2829  memset(&inst->src_ipaddr, 0, sizeof(inst->src_ipaddr));
2830 
2831  inst->src_ipaddr.af = inst->dst_ipaddr.af;
2832 
2833  if (inst->src_ipaddr.af == AF_INET) {
2834  inst->src_ipaddr.prefix = 32;
2835  } else {
2836  inst->src_ipaddr.prefix = 128;
2837  }
2838  }
2839 
2840  else if (inst->src_ipaddr.af != inst->dst_ipaddr.af) {
2841  cf_log_err(conf, "The 'ipaddr' and 'src_ipaddr' configuration items must "
2842  "be both of the same address family");
2843  return -1;
2844  }
2845 
2846  if (!inst->dst_port) {
2847  cf_log_err(conf, "A value must be given for 'port'");
2848  return -1;
2849  }
2850 
2851  /*
2852  * Clamp max_packet_size first before checking recv_buff and send_buff
2853  */
2854  FR_INTEGER_BOUND_CHECK("max_packet_size", inst->max_packet_size, >=, 64);
2855  FR_INTEGER_BOUND_CHECK("max_packet_size", inst->max_packet_size, <=, 65535);
2856 
2857 
2858 #ifdef __linux__
2859  if (inst->replicate) {
2860  /*
2861  * Replicating: Set the receive buffer to zero.
2862  */
2863  inst->recv_buff_is_set = true;
2864 
2865  /*
2866  * On Linux this has the effect of discarding
2867  * all incoming data in the kernel.
2868  * With macOS and others it's an invalid value.
2869  */
2870 
2871  inst->recv_buff = 0;
2872  } else {
2873 #endif
2874  if (inst->recv_buff_is_set) {
2875  FR_INTEGER_BOUND_CHECK("recv_buff", inst->recv_buff, >=, inst->max_packet_size);
2876  FR_INTEGER_BOUND_CHECK("recv_buff", inst->recv_buff, <=, (1 << 30));
2877  }
2878 #ifdef __linux__
2879  }
2880 #endif
2881 
2882  if (inst->send_buff_is_set) {
2883  FR_INTEGER_BOUND_CHECK("send_buff", inst->send_buff, >=, inst->max_packet_size);
2884  FR_INTEGER_BOUND_CHECK("send_buff", inst->send_buff, <=, (1 << 30));
2885  }
2886 
2887 
2888  return 0;
2889 }
2890 
2893  .common = {
2894  .magic = MODULE_MAGIC_INIT,
2895  .name = "radius_udp",
2896  .inst_size = sizeof(rlm_radius_udp_t),
2897 
2898  .thread_inst_size = sizeof(udp_thread_t),
2899  .thread_inst_type = "udp_thread_t",
2900 
2901  .config = module_config,
2902  .instantiate = mod_instantiate,
2903  .thread_instantiate = mod_thread_instantiate,
2904  },
2905  .enqueue = mod_enqueue,
2906  .signal = mod_signal,
2907  .resume = mod_resume,
2908 };
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
log_entry msg
Definition: acutest.h:794
int const char int line
Definition: acutest.h:702
#define RCSID(id)
Definition: build.h:444
#define NDEBUG_UNUSED
Definition: build.h:324
#define FALL_THROUGH
clang 10 doesn't recognised the FALL-THROUGH comment anymore
Definition: build.h:320
#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:378
#define UNUSED
Definition: build.h:313
#define CONF_PARSER_TERMINATOR
Definition: cf_parse.h:626
#define FR_INTEGER_BOUND_CHECK(_name, _var, _op, _bound)
Definition: cf_parse.h:486
#define FR_CONF_OFFSET(_name, _struct, _field)
conf_parser_t which parses a single CONF_PAIR, writing the result to a field in a struct
Definition: cf_parse.h:268
#define FR_CONF_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:406
#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:563
A section grouping multiple CONF_PAIR.
Definition: cf_priv.h:89
#define cf_log_err(_cf, _fmt,...)
Definition: cf_util.h:265
fr_connection_state_t
Definition: connection.h:45
@ FR_CONNECTION_STATE_CONNECTING
Waiting for connection to establish.
Definition: connection.h:50
@ FR_CONNECTION_STATE_FAILED
Connection has failed.
Definition: connection.h:54
@ FR_CONNECTION_STATE_INIT
Init state, sets up connection.
Definition: connection.h:49
@ FR_CONNECTION_STATE_CONNECTED
File descriptor is open (ready for writing).
Definition: connection.h:52
@ FR_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
static void * fr_dcursor_next(fr_dcursor_t *cursor)
Advanced the cursor to the next item.
Definition: dcursor.h:287
#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:208
#define fr_assert_fail(_msg,...)
Calls panic_action ifndef NDEBUG, else logs error.
Definition: debug.h:214
@ 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_DISCONNECT_REQUEST
RFC3575/RFC5176 - Disconnect-Request.
Definition: defs.h:46
@ 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_COA_REQUEST
RFC3575/RFC5176 - CoA-Request.
Definition: defs.h:49
@ 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_ACCOUNTING_REQUEST
RFC2866 - Accounting-Request.
Definition: defs.h:36
@ 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:250
fr_dict_t const ** out
Where to write a pointer to the loaded/resolved fr_dict_t.
Definition: dict.h:263
Specifies an attribute which must be present for the module to function.
Definition: dict.h:249
Specifies a dictionary which must be loaded/loadable for the module to function.
Definition: dict.h:262
void *_CONST data
Module instance's parsed configuration.
Definition: dl_module.h:165
#define MODULE_MAGIC_INIT
Stop people using different module/library/server versions together.
Definition: dl_module.h:65
CONF_SECTION *_CONST conf
Module's instance configuration.
Definition: dl_module.h:166
dl_module_inst_t const *_CONST parent
Parent module's instance (if any).
Definition: dl_module.h:167
#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:1340
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:75
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:821
#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 DEBUG_ENABLED
True if global debug level 1 messages are enabled.
Definition: log.h:257
#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:1489
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:1783
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:1604
int fr_event_fd_delete(fr_event_list_t *el, int fd, fr_event_filter_t filter)
Remove a file descriptor from the event loop.
Definition: event.c:1253
Stores all information relating to an event list.
Definition: event.c:411
A timer event.
Definition: event.c:102
fr_log_t default_log
Definition: log.c:290
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:599
@ 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:63
dl_module_inst_t const * inst
Dynamic loader API handle for the module.
Definition: module_ctx.h:52
void * thread
Thread instance data.
Definition: module_ctx.h:62
dl_module_inst_t const * inst
Dynamic loader API handle for the module.
Definition: module_ctx.h:59
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:51
Temporary structure to hold arguments for thread_instantiation calls.
Definition: module_ctx.h:58
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:137
#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:688
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:278
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:2978
int fr_pair_value_strdup(fr_pair_t *vp, char const *src, bool tainted)
Copy data into an "string" data type.
Definition: pair.c:2631
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:1340
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:1684
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:876
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:997
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:301
ssize_t fr_radius_encode(uint8_t *packet, size_t packet_len, uint8_t const *original, char const *secret, size_t secret_len, int code, int id, fr_pair_list_t *vps)
Encode VPS into a raw RADIUS packet.
Definition: base.c:860
char const * fr_radius_packet_name[FR_RADIUS_CODE_MAX]
Definition: base.c:94
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
#define RADIUS_AUTH_VECTOR_OFFSET
Definition: radius.h:32
char const * secret
Definition: radius.h:111
#define RADIUS_MAX_PACKET_SIZE
Definition: radius.h:40
#define RADIUS_MESSAGE_AUTHENTICATOR_LENGTH
Definition: radius.h:37
@ DECODE_FAIL_UNKNOWN
Definition: radius.h:74
@ DECODE_FAIL_NONE
Definition: radius.h:60
fr_radius_ctx_t * common
Definition: radius.h:137
TALLOC_CTX * tmp_ctx
for temporary things cleaned up during decoding
Definition: radius.h:141
static rs_t * conf
Definition: radsniff.c:53
uint32_t fr_rand(void)
Return a 32-bit random number.
Definition: rand.c:106
#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:315
static unsigned int hash(char const *username, unsigned int tablesize)
Definition: rlm_passwd.c:132
fr_time_delta_t revive_interval
Definition: rlm_radius.h:49
module_t common
Common fields to all loadable modules.
Definition: rlm_radius.h:82
fr_retry_config_t retry[FR_RADIUS_CODE_MAX]
Definition: rlm_radius.h:67
char const * name
Definition: rlm_radius.h:43
uint32_t status_check
code of status-check type
Definition: rlm_radius.h:61
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:81
uint32_t recv_buff
How big the kernel's receive buffer should be.
static void request_mux_replicate(UNUSED fr_event_list_t *el, fr_trunk_connection_t *tconn, fr_connection_t *conn, UNUSED void *uctx)
static void request_conn_release(fr_connection_t *conn, void *preq_to_reset, UNUSED void *uctx)
Clear out anything associated with the handle from the request.
static int8_t request_prioritise(void const *one, void const *two)
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.
fr_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)
static void thread_conn_notify_replicate(fr_trunk_connection_t *tconn, fr_connection_t *conn, fr_event_list_t *el, fr_trunk_connection_event_t notify_on, UNUSED void *uctx)
A special version of the trunk/event loop glue function which always discards incoming data.
uint16_t dst_port
Port of the home server.
static void request_mux(fr_event_list_t *el, fr_trunk_connection_t *tconn, fr_connection_t *conn, UNUSED void *uctx)
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 fr_connection_state_t conn_failed(void *handle, fr_connection_state_t state, UNUSED void *uctx)
Connection failed.
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)
#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 fr_connection_state_t conn_init(void **h_out, fr_connection_t *conn, void *uctx)
Initialise a new outbound connection.
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
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 request_cancel(UNUSED fr_connection_t *conn, void *preq_to_reset, fr_trunk_cancel_reason_t reason, UNUSED void *uctx)
Remove the request from any tracking structures.
static void conn_discard(UNUSED fr_event_list_t *el, int fd, UNUSED int flags, void *uctx)
Read and discard data.
fr_trunk_request_t * treq
Used for signalling.
rlm_radius_t * parent
rlm_radius instance.
static fr_dict_attr_t const * attr_proxy_state
static void thread_conn_notify(fr_trunk_connection_t *tconn, fr_connection_t *conn, fr_event_list_t *el, fr_trunk_connection_event_t notify_on, UNUSED void *uctx)
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 fr_connection_t * thread_conn_alloc(fr_trunk_connection_t *tconn, fr_event_list_t *el, fr_connection_conf_t const *conf, char const *log_prefix, void *uctx)
static void request_demux(UNUSED fr_event_list_t *el, fr_trunk_connection_t *tconn, fr_connection_t *conn, UNUSED void *uctx)
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 status_check_reply(fr_trunk_request_t *treq, fr_time_t now)
Deal with replies replies to status checks and possible negotiation.
static void status_check_retry(UNUSED fr_event_list_t *el, fr_time_t now, void *uctx)
static void request_fail(request_t *request, void *preq, void *rctx, NDEBUG_UNUSED fr_trunk_request_state_t state, UNUSED void *uctx)
Write out a canned failure.
static void request_retry(fr_event_list_t *el, fr_time_t now, void *uctx)
Handle retries when a request is being sent asynchronously.
static void request_conn_release_replicate(UNUSED fr_connection_t *conn, void *preq_to_reset, UNUSED void *uctx)
Clear out anything associated with the handle from the request.
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
static fr_dict_attr_t const * attr_acct_delay_time
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.
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)
static bool check_for_zombie(fr_event_list_t *el, fr_trunk_connection_t *tconn, fr_time_t now, fr_time_t last_sent)
See if the connection is zombied.
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
bool require_ma
saved from the original packet.
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.
fr_trunk_t * trunk
trunk handler
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.
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
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.
bool can_retransmit
can we retransmit this packet?
fr_event_timer_t const * zombie_ev
Zombie timeout.
bool status_checking
whether we're doing status checks
size_t buflen
Receive buffer length.
char const * module_name
the module that opened the connection
fr_trunk_request_t * treq
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.
Track the handle, which is tightly correlated with the FD.
Connect request_t to local tracking structure.
void fr_connection_signal_connected(fr_connection_t *conn)
Asynchronously signal that the connection is open.
Definition: connection.c:1136
int fr_connection_signal_on_fd(fr_connection_t *conn, int fd)
Setup the connection to change states to connected or failed based on I/O events.
Definition: connection.c:1400
fr_connection_t * fr_connection_alloc(TALLOC_CTX *ctx, fr_event_list_t *el, fr_connection_funcs_t const *funcs, fr_connection_conf_t const *conf, char const *log_prefix, void const *uctx)
Allocate a new connection.
Definition: connection.c:1507
void fr_connection_signal_reconnect(fr_connection_t *conn, fr_connection_reason_t reason)
Asynchronously signal the connection should be reconnected.
Definition: connection.c:1166
#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:796
#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 count
Definition: module.c:175
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
static int talloc_const_free(void const *ptr)
Free const'd memory.
Definition: talloc.h:212
#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:288
#define fr_time_eq(_a, _b)
Definition: time.h:241
static int64_t fr_time_delta_to_sec(fr_time_delta_t delta)
Definition: time.h:645
#define 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:686
#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 fr_trunk_request_free(fr_trunk_request_t **treq_to_free)
If the trunk request is freed then update the target requests.
Definition: trunk.c:2217
void fr_trunk_connection_signal_active(fr_trunk_connection_t *tconn)
Signal a trunk connection is no longer full.
Definition: trunk.c:3833
void fr_trunk_request_signal_cancel(fr_trunk_request_t *treq)
Cancel a trunk request.
Definition: trunk.c:2047
fr_trunk_enqueue_t fr_trunk_request_enqueue_on_conn(fr_trunk_request_t **treq_out, fr_trunk_connection_t *tconn, request_t *request, void *preq, void *rctx, bool ignore_limits)
Enqueue additional requests on a specific connection.
Definition: trunk.c:2629
fr_trunk_request_t * fr_trunk_request_alloc(fr_trunk_t *trunk, request_t *request)
(Pre-)Allocate a new trunk request
Definition: trunk.c:2369
void fr_trunk_request_signal_sent(fr_trunk_request_t *treq)
Signal that the request was written to a connection successfully.
Definition: trunk.c:1973
fr_trunk_enqueue_t fr_trunk_request_enqueue(fr_trunk_request_t **treq_out, fr_trunk_t *trunk, request_t *request, void *preq, void *rctx)
Enqueue a request that needs data written to the trunk.
Definition: trunk.c:2481
uint64_t fr_trunk_connection_requests_requeue(fr_trunk_connection_t *tconn, int states, uint64_t max, bool fail_bound)
Move requests off of a connection and requeue elsewhere.
Definition: trunk.c:1933
void fr_trunk_request_signal_complete(fr_trunk_request_t *treq)
Signal that a trunk request is complete.
Definition: trunk.c:1995
fr_trunk_enqueue_t fr_trunk_request_requeue(fr_trunk_request_t *treq)
Re-enqueue a request on the same connection.
Definition: trunk.c:2568
void fr_trunk_request_state_log(fr_log_t const *log, fr_log_type_t log_type, char const *file, int line, fr_trunk_request_t const *treq)
Definition: trunk.c:2716
fr_trunk_t * fr_trunk_alloc(TALLOC_CTX *ctx, fr_event_list_t *el, fr_trunk_io_funcs_t const *funcs, fr_trunk_conf_t const *conf, char const *log_prefix, void const *uctx, bool delay_start)
Allocate a new collection of connections.
Definition: trunk.c:4767
int fr_trunk_connection_pop_request(fr_trunk_request_t **treq_out, fr_trunk_connection_t *tconn)
Pop a request off a connection's pending queue.
Definition: trunk.c:3756
void fr_trunk_connection_signal_inactive(fr_trunk_connection_t *tconn)
Signal a trunk connection cannot accept more requests.
Definition: trunk.c:3810
void fr_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:3887
void fr_trunk_request_signal_fail(fr_trunk_request_t *treq)
Signal that a trunk request failed.
Definition: trunk.c:2027
void fr_trunk_connection_signal_reconnect(fr_trunk_connection_t *tconn, fr_connection_reason_t reason)
Signal a trunk connection is no longer viable.
Definition: trunk.c:3872
void fr_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:3904
Associates request queues with a connection.
Definition: trunk.c:127
Wraps a normal request.
Definition: trunk.c:97
Main trunk management handle.
Definition: trunk.c:189
fr_trunk_cancel_reason_t
Reasons for a request being cancelled.
Definition: trunk.h:55
@ FR_TRUNK_CANCEL_REASON_REQUEUE
A previously sent request is being requeued.
Definition: trunk.h:59
fr_trunk_request_state_t
Used for sanity checks and to simplify freeing.
Definition: trunk.h:161
@ FR_TRUNK_REQUEST_STATE_SENT
Was written to a socket. Waiting for a response.
Definition: trunk.h:172
@ FR_TRUNK_REQUEST_STATE_PENDING
In the queue of a connection and is pending writing.
Definition: trunk.h:168
@ FR_TRUNK_REQUEST_STATE_PARTIAL
Some of the request was written to the socket, more of it should be written later.
Definition: trunk.h:170
@ FR_TRUNK_REQUEST_STATE_INIT
Initial state.
Definition: trunk.h:162
fr_trunk_connection_alloc_t connection_alloc
Allocate a new fr_connection_t.
Definition: trunk.h:712
#define FR_TRUNK_REQUEST_STATE_ALL
All request states.
Definition: trunk.h:185
fr_trunk_connection_event_t
What type of I/O events the trunk connection is currently interested in receiving.
Definition: trunk.h:72
@ FR_TRUNK_CONN_EVENT_NONE
Don't notify the trunk on connection state changes.
Definition: trunk.h:73
@ FR_TRUNK_CONN_EVENT_WRITE
Trunk should be notified if a connection is writable.
Definition: trunk.h:77
@ FR_TRUNK_CONN_EVENT_READ
Trunk should be notified if a connection is readable.
Definition: trunk.h:75
@ FR_TRUNK_CONN_EVENT_BOTH
Trunk should be notified if a connection is readable or writable.
Definition: trunk.h:79
@ FR_TRUNK_ENQUEUE_IN_BACKLOG
Request should be enqueued in backlog.
Definition: trunk.h:149
@ FR_TRUNK_ENQUEUE_NO_CAPACITY
At maximum number of connections, and no connection has capacity.
Definition: trunk.h:151
@ FR_TRUNK_ENQUEUE_OK
Operation was successful.
Definition: trunk.h:150
@ FR_TRUNK_ENQUEUE_DST_UNAVAILABLE
Destination is down.
Definition: trunk.h:153
@ FR_TRUNK_ENQUEUE_FAIL
General failure.
Definition: trunk.h:154
Common configuration parameters for a trunk.
Definition: trunk.h:213
I/O functions to pass to fr_trunk_alloc.
Definition: trunk.h:711
close(uq->fd)
static fr_event_list_t * el
#define fr_pair_dcursor_by_da_init(_cursor, _list, _da)
Initialise a cursor that will return only attributes matching the specified fr_dict_attr_t.
Definition: pair.h:627
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:844
fr_retry_state_t fr_retry_next(fr_retry_t *r, fr_time_t now)
Initialize a retransmission counter.
Definition: retry.c:84
void fr_retry_init(fr_retry_t *r, fr_time_t now, fr_retry_config_t const *config)
Initialize a retransmission counter.
Definition: retry.c:36
fr_time_t start
when we started the retransmission
Definition: retry.h:43
fr_time_delta_t rt
retransmit interval
Definition: retry.h:46
uint32_t mrc
Maximum retransmission count.
Definition: retry.h:36
fr_retry_config_t const * config
master configuration
Definition: retry.h:42
@ FR_RETRY_MRC
reached maximum retransmission count
Definition: retry.h:57
@ FR_RETRY_CONTINUE
Definition: retry.h:56
@ FR_RETRY_MRD
reached maximum retransmission duration
Definition: retry.h:58
uint32_t count
number of sent packets
Definition: retry.h:47
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:45
fr_time_t next
when the next timer should be set
Definition: retry.h:44
char const * fr_strerror(void)
Get the last library error.
Definition: strerror.c:554
#define fr_box_ipaddr(_val)
Definition: value.h:287
static fr_slen_t data
Definition: value.h:1259
#define fr_box_time_delta(_val)
Definition: value.h:336
int nonnull(2, 5))
static size_t char ** out
Definition: value.h:984