All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
packet.c
Go to the documentation of this file.
1 /*
2  * packet.c Generic packet manipulation functions.
3  *
4  * Version: $Id: 767168e032ce13288897a8657f2bd0ef6b42118e $
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  *
20  * Copyright 2000-2006 The FreeRADIUS server project
21  */
22 
23 RCSID("$Id: 767168e032ce13288897a8657f2bd0ef6b42118e $")
24 
25 #include <freeradius-devel/libradius.h>
26 
27 #ifdef WITH_UDPFROMTO
28 #include <freeradius-devel/udpfromto.h>
29 #endif
30 
31 #include <fcntl.h>
32 
33 /*
34  * See if two packets are identical.
35  *
36  * Note that we do NOT compare the authentication vectors.
37  * That's because if the authentication vector is different,
38  * it means that the NAS has given up on the earlier request.
39  */
40 int fr_packet_cmp(RADIUS_PACKET const *a, RADIUS_PACKET const *b)
41 {
42  int rcode;
43 
44  /*
45  * 256-way fanout.
46  */
47  if (a->id < b->id) return -1;
48  if (a->id > b->id) return +1;
49 
50  if (a->sockfd < b->sockfd) return -1;
51  if (a->sockfd > b->sockfd) return +1;
52 
53  /*
54  * Source ports are pretty much random.
55  */
56  rcode = (int) a->src_port - (int) b->src_port;
57  if (rcode != 0) return rcode;
58 
59  /*
60  * Usually many client IPs, and few server IPs
61  */
62  rcode = fr_ipaddr_cmp(&a->src_ipaddr, &b->src_ipaddr);
63  if (rcode != 0) return rcode;
64 
65  /*
66  * One socket can receive packets for multiple
67  * destination IPs, so we check that before checking the
68  * file descriptor.
69  */
70  rcode = fr_ipaddr_cmp(&a->dst_ipaddr, &b->dst_ipaddr);
71  if (rcode != 0) return rcode;
72 
73  /*
74  * At this point, the order of comparing socket FDs
75  * and/or destination ports doesn't matter. One of those
76  * fields will make the socket unique, and the other is
77  * pretty much redundant.
78  */
79  rcode = (int) a->dst_port - (int) b->dst_port;
80  return rcode;
81 }
82 
83 /** Determine if an address is the INADDR_ANY address for its address family
84  *
85  * @param ipaddr to check.
86  * @return
87  * - 0 if it's not.
88  * - 1 if it is.
89  * - -1 on error.
90  */
92 {
93 
94  if (ipaddr->af == AF_INET) {
95  if (ipaddr->ipaddr.ip4addr.s_addr == INADDR_ANY) {
96  return 1;
97  }
98 
99 #ifdef HAVE_STRUCT_SOCKADDR_IN6
100  } else if (ipaddr->af == AF_INET6) {
101  if (IN6_IS_ADDR_UNSPECIFIED(&(ipaddr->ipaddr.ip6addr))) {
102  return 1;
103  }
104 #endif
105 
106  } else {
107  fr_strerror_printf("Unknown address family");
108  return -1;
109  }
110 
111  return 0;
112 }
113 
114 
115 /*
116  * Create a fake "request" from a reply, for later lookup.
117  */
119  RADIUS_PACKET const *reply)
120 {
121  request->sockfd = reply->sockfd;
122  request->id = reply->id;
123 #ifdef WITH_TCP
124  request->proto = reply->proto;
125 #endif
126  request->src_port = reply->dst_port;
127  request->dst_port = reply->src_port;
128  request->src_ipaddr = reply->dst_ipaddr;
129  request->dst_ipaddr = reply->src_ipaddr;
130  request->if_index = reply->if_index;
131 }
132 
133 /*
134  * Open a socket on the given IP and port.
135  */
136 int fr_socket(fr_ipaddr_t *ipaddr, uint16_t port)
137 {
138  int sockfd;
139  struct sockaddr_storage salocal;
140  socklen_t salen;
141 
142  sockfd = socket(ipaddr->af, SOCK_DGRAM, 0);
143  if (sockfd < 0) {
144  fr_strerror_printf("cannot open socket: %s", fr_syserror(errno));
145  return sockfd;
146  }
147 
148 #ifdef WITH_UDPFROMTO
149  /*
150  * Initialize udpfromto for all sockets.
151  */
152  if (udpfromto_init(sockfd) != 0) {
153  close(sockfd);
154  fr_strerror_printf("cannot initialize udpfromto: %s", fr_syserror(errno));
155  return -1;
156  }
157 #endif
158 
159  if (!fr_ipaddr_to_sockaddr(ipaddr, port, &salocal, &salen)) {
160  return sockfd;
161  }
162 
163 #ifdef HAVE_STRUCT_SOCKADDR_IN6
164  if (ipaddr->af == AF_INET6) {
165  /*
166  * Listening on '::' does NOT get you IPv4 to
167  * IPv6 mapping. You've got to listen on an IPv4
168  * address, too. This makes the rest of the server
169  * design a little simpler.
170  */
171 #ifdef IPV6_V6ONLY
172 
173  if (IN6_IS_ADDR_UNSPECIFIED(&ipaddr->ipaddr.ip6addr)) {
174  int on = 1;
175 
176  if (setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY,
177  (char *)&on, sizeof(on)) < 0) {
178  close(sockfd);
179  fr_strerror_printf("Failed setting sockopt "
180  "IPPROTO_IPV6 - IPV6_V6ONLY"
181  ": %s", fr_syserror(errno));
182  return -1;
183  }
184  }
185 #endif /* IPV6_V6ONLY */
186  }
187 #endif /* HAVE_STRUCT_SOCKADDR_IN6 */
188 
189 #if (defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)) || defined(IP_DONTFRAG)
190  if (ipaddr->af == AF_INET) {
191  int flag;
192 
193 #if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
194 
195  /*
196  * Disable PMTU discovery. On Linux, this
197  * also makes sure that the "don't fragment"
198  * flag is zero.
199  */
200  flag = IP_PMTUDISC_DONT;
201  if (setsockopt(sockfd, IPPROTO_IP, IP_MTU_DISCOVER,
202  &flag, sizeof(flag)) < 0) {
203  close(sockfd);
204  fr_strerror_printf("Failed setting sockopt "
205  "IPPROTO_IP - IP_MTU_DISCOVER: %s",
206  fr_syserror(errno));
207  return -1;
208  }
209 #endif
210 
211 #if defined(IP_DONTFRAG)
212  /*
213  * Ensure that the "don't fragment" flag is zero.
214  */
215  flag = 0;
216  if (setsockopt(sockfd, IPPROTO_IP, IP_DONTFRAG,
217  &flag, sizeof(flag)) < 0) {
218  close(sockfd);
219  fr_strerror_printf("Failed setting sockopt "
220  "IPPROTO_IP - IP_DONTFRAG: %s",
221  fr_syserror(errno));
222  return -1;
223  }
224 #endif
225  }
226 #endif
227 
228  if (bind(sockfd, (struct sockaddr *) &salocal, salen) < 0) {
229  close(sockfd);
230  fr_strerror_printf("Cannot bind socket: %s", fr_syserror(errno));
231  return -1;
232  }
233 
234  return sockfd;
235 }
236 
237 
238 /*
239  * We need to keep track of the socket & it's IP/port.
240  */
241 typedef struct fr_packet_socket_t {
242  int sockfd;
243  void *ctx;
244 
245  uint32_t num_outgoing;
246 
247  int src_any;
249  uint16_t src_port;
250 
251  int dst_any;
253  uint16_t dst_port;
254 
255  bool dont_use;
256 
257 #ifdef WITH_TCP
258  int proto;
259 #endif
260 
261  uint8_t id[32];
263 
264 
265 #define FNV_MAGIC_PRIME (0x01000193)
266 #define MAX_SOCKETS (256)
267 #define SOCKOFFSET_MASK (MAX_SOCKETS - 1)
268 #define SOCK2OFFSET(sockfd) ((sockfd * FNV_MAGIC_PRIME) & SOCKOFFSET_MASK)
269 
270 /*
271  * Structure defining a list of packets (incoming or outgoing)
272  * that should be managed.
273  */
276 
277  int alloc_id;
278  uint32_t num_outgoing;
281 
283 };
284 
285 
286 /*
287  * Ugh. Doing this on every sent/received packet is not nice.
288  */
290  int sockfd)
291 {
292  int i, start;
293 
294  i = start = SOCK2OFFSET(sockfd);
295 
296  do { /* make this hack slightly more efficient */
297  if (pl->sockets[i].sockfd == sockfd) return &pl->sockets[i];
298 
299  i = (i + 1) & SOCKOFFSET_MASK;
300  } while (i != start);
301 
302  return NULL;
303 }
304 
306 {
307  fr_packet_socket_t *ps;
308 
309  if (!pl) {
310  fr_strerror_printf("Invalid argument");
311  return false;
312  }
313 
314  ps = fr_socket_find(pl, sockfd);
315  if (!ps) {
316  fr_strerror_printf("No such socket");
317  return false;
318  }
319 
320  ps->dont_use = true;
321  return true;
322 }
323 
325 {
326  fr_packet_socket_t *ps;
327 
328  if (!pl) return false;
329 
330  ps = fr_socket_find(pl, sockfd);
331  if (!ps) return false;
332 
333  ps->dont_use = false;
334  return true;
335 }
336 
337 
339 {
340  fr_packet_socket_t *ps;
341 
342  if (!pl) return false;
343 
344  ps = fr_socket_find(pl, sockfd);
345  if (!ps) return false;
346 
347  if (ps->num_outgoing != 0) return false;
348 
349  ps->sockfd = -1;
350  pl->num_sockets--;
351 
352  return true;
353 }
354 
355 
357  fr_ipaddr_t *dst_ipaddr, uint16_t dst_port,
358  void *ctx)
359 {
360  int i, start;
361  struct sockaddr_storage src;
362  socklen_t sizeof_src;
363  fr_packet_socket_t *ps;
364 
365  if (!pl || !dst_ipaddr || (dst_ipaddr->af == AF_UNSPEC)) {
366  fr_strerror_printf("Invalid argument");
367  return false;
368  }
369 
370  if (pl->num_sockets >= MAX_SOCKETS) {
371  fr_strerror_printf("Too many open sockets");
372  return false;
373  }
374 
375 #ifndef WITH_TCP
376  if (proto != IPPROTO_UDP) {
377  fr_strerror_printf("only UDP is supported");
378  return false;
379  }
380 #endif
381 
382  ps = NULL;
383  i = start = SOCK2OFFSET(sockfd);
384 
385  do {
386  if (pl->sockets[i].sockfd == -1) {
387  ps = &pl->sockets[i];
388  break;
389  }
390 
391  i = (i + 1) & SOCKOFFSET_MASK;
392  } while (i != start);
393 
394  if (!ps) {
395  fr_strerror_printf("All socket entries are full");
396  return false;
397  }
398 
399  memset(ps, 0, sizeof(*ps));
400  ps->ctx = ctx;
401 #ifdef WITH_TCP
402  ps->proto = proto;
403 #endif
404 
405  /*
406  * Get address family, etc. first, so we know if we
407  * need to do udpfromto.
408  *
409  * FIXME: udpfromto also does this, but it's not
410  * a critical problem.
411  */
412  sizeof_src = sizeof(src);
413  memset(&src, 0, sizeof_src);
414  if (getsockname(sockfd, (struct sockaddr *) &src,
415  &sizeof_src) < 0) {
416  fr_strerror_printf("%s", fr_syserror(errno));
417  return false;
418  }
419 
420  if (!fr_ipaddr_from_sockaddr(&src, sizeof_src, &ps->src_ipaddr,
421  &ps->src_port)) {
422  fr_strerror_printf("Failed to get IP");
423  return false;
424  }
425 
426  ps->dst_ipaddr = *dst_ipaddr;
427  ps->dst_port = dst_port;
428 
429  ps->src_any = fr_is_inaddr_any(&ps->src_ipaddr);
430  if (ps->src_any < 0) return false;
431 
432  ps->dst_any = fr_is_inaddr_any(&ps->dst_ipaddr);
433  if (ps->dst_any < 0) return false;
434 
435  /*
436  * As the last step before returning.
437  */
438  ps->sockfd = sockfd;
439  pl->num_sockets++;
440 
441  return true;
442 }
443 
444 static int packet_entry_cmp(void const *one, void const *two)
445 {
446  RADIUS_PACKET const * const *a = one;
447  RADIUS_PACKET const * const *b = two;
448 
449  return fr_packet_cmp(*a, *b);
450 }
451 
453 {
454  if (!pl) return;
455 
456  rbtree_free(pl->tree);
457  free(pl);
458 }
459 
460 
461 /*
462  * Caller is responsible for managing the packet entries.
463  */
465 {
466  int i;
468 
469  pl = malloc(sizeof(*pl));
470  if (!pl) return NULL;
471  memset(pl, 0, sizeof(*pl));
472 
473  pl->tree = rbtree_create(NULL, packet_entry_cmp, NULL, 0);
474  if (!pl->tree) {
476  return NULL;
477  }
478 
479  for (i = 0; i < MAX_SOCKETS; i++) {
480  pl->sockets[i].sockfd = -1;
481  }
482 
483  pl->alloc_id = alloc_id;
484 
485  return pl;
486 }
487 
488 
489 /*
490  * If pl->alloc_id is set, then fr_packet_list_id_alloc() MUST
491  * be called before inserting the packet into the list!
492  */
494  RADIUS_PACKET **request_p)
495 {
496  if (!pl || !request_p || !*request_p) return 0;
497 
498  return rbtree_insert(pl->tree, request_p);
499 }
500 
502  RADIUS_PACKET *request)
503 {
504  if (!pl || !request) return 0;
505 
506  return rbtree_finddata(pl->tree, &request);
507 }
508 
509 
510 /*
511  * This presumes that the reply has dst_ipaddr && dst_port set up
512  * correctly (i.e. real IP, or "*").
513  */
515 {
516  RADIUS_PACKET my_request, *request;
517  fr_packet_socket_t *ps;
518 
519  if (!pl || !reply) return NULL;
520 
521  ps = fr_socket_find(pl, reply->sockfd);
522  if (!ps) return NULL;
523 
524  /*
525  * Initialize request from reply, AND from the source
526  * IP & port of this socket. The client may have bound
527  * the socket to 0, in which case it's some random port,
528  * that is NOT in the original request->src_port.
529  */
530  my_request.sockfd = reply->sockfd;
531  my_request.id = reply->id;
532 
533 #ifdef WITH_TCP
534  /*
535  * TCP sockets are always bound to the correct src/dst IP/port
536  */
537  if (ps->proto == IPPROTO_TCP) {
538  reply->dst_ipaddr = ps->src_ipaddr;
539  reply->dst_port = ps->src_port;
540  reply->src_ipaddr = ps->dst_ipaddr;
541  reply->src_port = ps->dst_port;
542 
543  my_request.src_ipaddr = ps->src_ipaddr;
544  my_request.src_port = ps->src_port;
545  my_request.dst_ipaddr = ps->dst_ipaddr;
546  my_request.dst_port = ps->dst_port;
547 
548  } else
549 #endif
550  {
551  if (ps->src_any) {
552  my_request.src_ipaddr = ps->src_ipaddr;
553  } else {
554  my_request.src_ipaddr = reply->dst_ipaddr;
555  }
556  my_request.src_port = ps->src_port;
557 
558  my_request.dst_ipaddr = reply->src_ipaddr;
559  my_request.dst_port = reply->src_port;
560  }
561 
562 #ifdef WITH_TCP
563  my_request.proto = reply->proto;
564 #endif
565  request = &my_request;
566 
567  return rbtree_finddata(pl->tree, &request);
568 }
569 
570 
572 {
573  rbnode_t *node;
574 
575  if (!pl || !request) return false;
576 
577  node = rbtree_find(pl->tree, &request);
578  if (!node) return false;
579 
580  rbtree_delete(pl->tree, node);
581  return true;
582 }
583 
585 {
586  if (!pl) return 0;
587 
588  return rbtree_num_elements(pl->tree);
589 }
590 
591 
592 /*
593  * 1 == ID was allocated & assigned
594  * 0 == couldn't allocate ID.
595  *
596  * Note that this ALSO assigns a socket to use, and updates
597  * packet->request->src_ipaddr && packet->request->src_port
598  *
599  * In multi-threaded systems, the calls to id_alloc && id_free
600  * should be protected by a mutex. This does NOT have to be
601  * the same mutex as the one protecting the insert/find/yank
602  * calls!
603  *
604  * We assume that the packet has dst_ipaddr && dst_port
605  * already initialized. We will use those to find an
606  * outgoing socket. The request MAY also have src_ipaddr set.
607  *
608  * We also assume that the sender doesn't care which protocol
609  * should be used.
610  */
612  RADIUS_PACKET **request_p, void **pctx)
613 {
614  int i, j, k, fd, id, start_i, start_j, start_k;
615  int src_any = 0;
616  fr_packet_socket_t *ps= NULL;
617  RADIUS_PACKET *request = *request_p;
618 
619  if ((request->dst_ipaddr.af == AF_UNSPEC) ||
620  (request->dst_port == 0)) {
621  fr_strerror_printf("No destination address/port specified");
622  return false;
623  }
624 
625 #ifndef WITH_TCP
626  if ((proto != 0) && (proto != IPPROTO_UDP)) {
627  fr_strerror_printf("Invalid destination protocol");
628  return false;
629  }
630 #endif
631 
632  /*
633  * Special case: unspec == "don't care"
634  */
635  if (request->src_ipaddr.af == AF_UNSPEC) {
636  memset(&request->src_ipaddr, 0, sizeof(request->src_ipaddr));
637  request->src_ipaddr.af = request->dst_ipaddr.af;
638  }
639 
640  src_any = fr_is_inaddr_any(&request->src_ipaddr);
641  if (src_any < 0) {
642  fr_strerror_printf("Can't check src_ipaddr");
643  return false;
644  }
645 
646  /*
647  * MUST specify a destination address.
648  */
649  if (fr_is_inaddr_any(&request->dst_ipaddr) != 0) {
650  fr_strerror_printf("Must specify a dst_ipaddr");
651  return false;
652  }
653 
654  /*
655  * FIXME: Go to an LRU system. This prevents ID re-use
656  * for as long as possible. The main problem with that
657  * approach is that it requires us to populate the
658  * LRU/FIFO when we add a new socket, or a new destination,
659  * which can be expensive.
660  *
661  * The LRU can be avoided if the caller takes care to free
662  * Id's only when all responses have been received, OR after
663  * a timeout.
664  *
665  * Right now, the random approach is almost OK... it's
666  * brute-force over all of the available ID's, BUT using
667  * random numbers for everything spreads the load a bit.
668  *
669  * The old method had a hash lookup on allocation AND
670  * on free. The new method has brute-force on allocation,
671  * and near-zero cost on free.
672  */
673 
674  id = fd = -1;
675  start_i = fr_rand() & SOCKOFFSET_MASK;
676 
677 #define ID_i ((i + start_i) & SOCKOFFSET_MASK)
678  for (i = 0; i < MAX_SOCKETS; i++) {
679  if (pl->sockets[ID_i].sockfd == -1) continue; /* paranoia */
680 
681  ps = &(pl->sockets[ID_i]);
682 
683  /*
684  * This socket is marked as "don't use for new
685  * packets". But we can still receive packets
686  * that are outstanding.
687  */
688  if (ps->dont_use) continue;
689 
690  /*
691  * All IDs are allocated: ignore it.
692  */
693  if (ps->num_outgoing == 256) continue;
694 
695 #ifdef WITH_TCP
696  if (ps->proto != proto) continue;
697 #endif
698 
699  /*
700  * Address families don't match, skip it.
701  */
702  if (ps->src_ipaddr.af != request->dst_ipaddr.af) continue;
703 
704  /*
705  * MUST match dst port, if we have one.
706  */
707  if ((ps->dst_port != 0) &&
708  (ps->dst_port != request->dst_port)) continue;
709 
710  /*
711  * MUST match requested src port, if one has been given.
712  */
713  if ((request->src_port != 0) &&
714  (ps->src_port != request->src_port)) continue;
715 
716  /*
717  * We don't care about the source IP, but this
718  * socket is link local, and the requested
719  * destination is not link local. Ignore it.
720  */
721  if (src_any && (ps->src_ipaddr.af == AF_INET) &&
722  (((ps->src_ipaddr.ipaddr.ip4addr.s_addr >> 24) & 0xff) == 127) &&
723  (((request->dst_ipaddr.ipaddr.ip4addr.s_addr >> 24) & 0xff) != 127)) continue;
724 
725  /*
726  * We're sourcing from *, and they asked for a
727  * specific source address: ignore it.
728  */
729  if (ps->src_any && !src_any) continue;
730 
731  /*
732  * We're sourcing from a specific IP, and they
733  * asked for a source IP that isn't us: ignore
734  * it.
735  */
736  if (!ps->src_any && !src_any &&
737  (fr_ipaddr_cmp(&request->src_ipaddr,
738  &ps->src_ipaddr) != 0)) continue;
739 
740  /*
741  * UDP sockets are allowed to match
742  * destination IPs exactly, OR a socket
743  * with destination * is allowed to match
744  * any requested destination.
745  *
746  * TCP sockets must match the destination
747  * exactly. They *always* have dst_any=0,
748  * so the first check always matches.
749  */
750  if (!ps->dst_any &&
751  (fr_ipaddr_cmp(&request->dst_ipaddr,
752  &ps->dst_ipaddr) != 0)) continue;
753 
754  /*
755  * Otherwise, this socket is OK to use.
756  */
757 
758  /*
759  * Look for a free Id, starting from a random number.
760  */
761  start_j = fr_rand() & 0x1f;
762 #define ID_j ((j + start_j) & 0x1f)
763  for (j = 0; j < 32; j++) {
764  if (ps->id[ID_j] == 0xff) continue;
765 
766 
767  start_k = fr_rand() & 0x07;
768 #define ID_k ((k + start_k) & 0x07)
769  for (k = 0; k < 8; k++) {
770  if ((ps->id[ID_j] & (1 << ID_k)) != 0) continue;
771 
772  ps->id[ID_j] |= (1 << ID_k);
773  id = (ID_j * 8) + ID_k;
774  fd = i;
775  break;
776  }
777  if (fd >= 0) break;
778  }
779 #undef ID_i
780 #undef ID_j
781 #undef ID_k
782  break;
783  }
784 
785  /*
786  * Ask the caller to allocate a new ID.
787  */
788  if (fd < 0) {
789  fr_strerror_printf("Failed finding socket, caller must allocate a new one");
790  return false;
791  }
792 
793  /*
794  * Set the ID, source IP, and source port.
795  */
796  request->id = id;
797 
798  request->sockfd = ps->sockfd;
799  request->src_ipaddr = ps->src_ipaddr;
800  request->src_port = ps->src_port;
801 
802  /*
803  * If we managed to insert it, we're done.
804  */
805  if (fr_packet_list_insert(pl, request_p)) {
806  if (pctx) *pctx = ps->ctx;
807  ps->num_outgoing++;
808  pl->num_outgoing++;
809  return true;
810  }
811 
812  /*
813  * Mark the ID as free. This is the one line from
814  * id_free() that we care about here.
815  */
816  ps->id[(request->id >> 3) & 0x1f] &= ~(1 << (request->id & 0x07));
817 
818  request->id = -1;
819  request->sockfd = -1;
820  request->src_ipaddr.af = AF_UNSPEC;
821  request->src_port = 0;
822 
823  return false;
824 }
825 
826 /*
827  * Should be called AFTER yanking it from the list, so that
828  * any newly inserted entries don't collide with this one.
829  */
831  RADIUS_PACKET *request, bool yank)
832 {
833  fr_packet_socket_t *ps;
834 
835  if (!pl || !request) return false;
836 
837  if (yank && !fr_packet_list_yank(pl, request)) return false;
838 
839  ps = fr_socket_find(pl, request->sockfd);
840  if (!ps) return false;
841 
842 #if 0
843  if (!ps->id[(request->id >> 3) & 0x1f] & (1 << (request->id & 0x07))) {
844  fr_exit(1);
845  }
846 #endif
847 
848  ps->id[(request->id >> 3) & 0x1f] &= ~(1 << (request->id & 0x07));
849 
850  ps->num_outgoing--;
851  pl->num_outgoing--;
852 
853  request->id = -1;
854  request->src_ipaddr.af = AF_UNSPEC; /* id_alloc checks this */
855  request->src_port = 0;
856 
857  return true;
858 }
859 
860 /*
861  * We always walk RBTREE_DELETE_ORDER, which is like RBTREE_IN_ORDER, except that
862  * <0 means error, stop
863  * 0 means OK, continue
864  * 1 means delete current node and stop
865  * 2 means delete current node and continue
866  */
868 {
869  if (!pl || !callback) return 0;
870 
871  return rbtree_walk(pl->tree, RBTREE_DELETE_ORDER, callback, ctx);
872 }
873 
875 {
876  int i, maxfd;
877 
878  if (!pl || !set) return 0;
879 
880  maxfd = -1;
881 
882  for (i = 0; i < MAX_SOCKETS; i++) {
883  if (pl->sockets[i].sockfd == -1) continue;
884  FD_SET(pl->sockets[i].sockfd, set);
885  if (pl->sockets[i].sockfd > maxfd) {
886  maxfd = pl->sockets[i].sockfd;
887  }
888  }
889 
890  if (maxfd < 0) return -1;
891 
892  return maxfd + 1;
893 }
894 
895 /*
896  * Round-robins the receivers, without priority.
897  *
898  * FIXME: Add sockfd, if -1, do round-robin, else do sockfd
899  * IF in fdset.
900  */
902 {
903  int start;
904  RADIUS_PACKET *packet;
905 
906  if (!pl || !set) return NULL;
907 
908  start = pl->last_recv;
909  do {
910  start++;
911  start &= SOCKOFFSET_MASK;
912 
913  if (pl->sockets[start].sockfd == -1) continue;
914 
915  if (!FD_ISSET(pl->sockets[start].sockfd, set)) continue;
916 
917 #ifdef WITH_TCP
918  if (pl->sockets[start].proto == IPPROTO_TCP) {
919  packet = fr_tcp_recv(pl->sockets[start].sockfd, 0);
920  } else
921 #endif
922  packet = fr_radius_recv(NULL, pl->sockets[start].sockfd, 0);
923  if (!packet) continue;
924 
925  /*
926  * Call fr_packet_list_find_byreply(). If it
927  * doesn't find anything, discard the reply.
928  */
929 
930  pl->last_recv = start;
931 #ifdef WITH_TCP
932  packet->proto = pl->sockets[start].proto;
933 #endif
934  return packet;
935  } while (start != pl->last_recv);
936 
937  return NULL;
938 }
939 
941 {
942  uint32_t num_elements;
943 
944  if (!pl) return 0;
945 
946  num_elements = rbtree_num_elements(pl->tree);
947  if (num_elements < pl->num_outgoing) return 0; /* panic! */
948 
949  return num_elements - pl->num_outgoing;
950 }
951 
953 {
954  if (!pl) return 0;
955 
956  return pl->num_outgoing;
957 }
958 
959 /*
960  * Debug the packet if requested.
961  */
962 void fr_packet_header_print(FILE *fp, RADIUS_PACKET *packet, bool received)
963 {
964  char src_ipaddr[FR_IPADDR_STRLEN];
965  char dst_ipaddr[FR_IPADDR_STRLEN];
966 #if defined(WITH_UDPFROMTO) && defined(WITH_IFINDEX_NAME_RESOLUTION)
967  char if_name[IFNAMSIZ];
968 #endif
969 
970  if (!fp) return;
971  if (!packet) return;
972 
973  /*
974  * Client-specific debugging re-prints the input
975  * packet into the client log.
976  *
977  * This really belongs in a utility library
978  */
979  if (is_radius_code(packet->code)) {
980  fprintf(fp, "%s %s Id %i from %s%s%s:%i to %s%s%s:%i "
981 #if defined(WITH_UDPFROMTO) && defined(WITH_IFINDEX_NAME_RESOLUTION)
982  "%s%s%s"
983 #endif
984  "length %zu\n",
985  received ? "Received" : "Sent",
986  fr_packet_codes[packet->code],
987  packet->id,
988  packet->src_ipaddr.af == AF_INET6 ? "[" : "",
989  fr_inet_ntop(src_ipaddr, sizeof(src_ipaddr), &packet->src_ipaddr),
990  packet->src_ipaddr.af == AF_INET6 ? "]" : "",
991  packet->src_port,
992  packet->dst_ipaddr.af == AF_INET6 ? "[" : "",
993  fr_inet_ntop(dst_ipaddr, sizeof(dst_ipaddr), &packet->dst_ipaddr),
994  packet->dst_ipaddr.af == AF_INET6 ? "]" : "",
995  packet->dst_port,
996 #if defined(WITH_UDPFROMTO) && defined(WITH_IFINDEX_NAME_RESOLUTION)
997  received ? "via " : "",
998  received ? fr_ifname_from_ifindex(if_name, packet->if_index) : "",
999  received ? " " : "",
1000 #endif
1001  packet->data_len);
1002  } else {
1003  fprintf(fp, "%s code %u Id %i from %s%s%s:%i to %s%s%s:%i "
1004 #if defined(WITH_UDPFROMTO) && defined(WITH_IFINDEX_NAME_RESOLUTION)
1005  "%s%s%s"
1006 #endif
1007  "length %zu\n",
1008  received ? "Received" : "Sent",
1009  packet->code,
1010  packet->id,
1011  packet->src_ipaddr.af == AF_INET6 ? "[" : "",
1012  fr_inet_ntop(src_ipaddr, sizeof(src_ipaddr), &packet->src_ipaddr),
1013  packet->src_ipaddr.af == AF_INET6 ? "]" : "",
1014  packet->src_port,
1015  packet->dst_ipaddr.af == AF_INET6 ? "[" : "",
1016  fr_inet_ntop(dst_ipaddr, sizeof(dst_ipaddr), &packet->dst_ipaddr),
1017  packet->dst_ipaddr.af == AF_INET6 ? "]" : "",
1018  packet->dst_port,
1019 #if defined(WITH_UDPFROMTO) && defined(WITH_IFINDEX_NAME_RESOLUTION)
1020  received ? "via " : "",
1021  received ? fr_ifname_from_ifindex(if_name, packet->if_index) : "",
1022  received ? " " : "",
1023 #endif
1024  packet->data_len);
1025  }
1026 }
1027 
int sockfd
Socket this packet was read from.
Definition: libradius.h:147
static int sockfd
Definition: radclient.c:59
int id
Packet ID (used to link requests/responses).
Definition: libradius.h:154
static fr_packet_socket_t * fr_socket_find(fr_packet_list_t *pl, int sockfd)
Definition: packet.c:289
void rbtree_free(rbtree_t *tree)
Definition: rbtree.c:84
uint16_t src_port
Definition: packet.c:249
uint32_t fr_packet_list_num_elements(fr_packet_list_t *pl)
Definition: packet.c:584
int fr_ipaddr_from_sockaddr(struct sockaddr_storage const *sa, socklen_t salen, fr_ipaddr_t *ipaddr, uint16_t *port)
Definition: inet.c:1095
rbnode_t * rbtree_find(rbtree_t *tree, void const *data)
Find an element in the tree, returning the data, not the node.
Definition: rbtree.c:511
bool fr_packet_list_socket_del(fr_packet_list_t *pl, int sockfd)
Definition: packet.c:338
char const * fr_packet_codes[FR_MAX_PACKET_CODE]
Definition: radius.c:101
void fr_request_from_reply(RADIUS_PACKET *request, RADIUS_PACKET const *reply)
Definition: packet.c:118
uint32_t fr_rand(void)
Return a 32-bit random number.
Definition: radius.c:1621
fr_ipaddr_t src_ipaddr
Definition: packet.c:248
fr_ipaddr_t src_ipaddr
Src IP address of packet.
Definition: libradius.h:149
uint32_t fr_packet_list_num_outgoing(fr_packet_list_t *pl)
Definition: packet.c:952
bool fr_packet_list_id_alloc(fr_packet_list_t *pl, int proto, RADIUS_PACKET **request_p, void **pctx)
Definition: packet.c:611
rbtree_t * tree
Definition: packet.c:275
void * rbtree_finddata(rbtree_t *tree, void const *data)
Find the user data.
Definition: rbtree.c:537
bool fr_packet_list_socket_thaw(fr_packet_list_t *pl, int sockfd)
Definition: packet.c:324
uint16_t dst_port
Definition: packet.c:253
uint16_t dst_port
DST Port of packet.
Definition: libradius.h:152
uint16_t src_port
Src port of packet.
Definition: libradius.h:151
fr_ipaddr_t dst_ipaddr
Dst IP address of packet.
Definition: libradius.h:150
struct fr_packet_socket_t fr_packet_socket_t
static char const * proto
Definition: radclient.c:63
#define FR_IPADDR_STRLEN
Like INET6_ADDRSTRLEN but includes space for the textual Zone ID.
Definition: inet.h:67
int af
Address family.
Definition: inet.h:42
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
Definition: log.c:238
rbtree_t * rbtree_create(TALLOC_CTX *ctx, rb_comparator_t compare, rb_free_t node_free, int flags)
Create a new RED-BLACK tree.
Definition: rbtree.c:112
bool fr_packet_list_socket_freeze(fr_packet_list_t *pl, int sockfd)
Definition: packet.c:305
fr_packet_socket_t sockets[MAX_SOCKETS]
Definition: packet.c:282
void fr_packet_header_print(FILE *fp, RADIUS_PACKET *packet, bool received)
Definition: packet.c:962
union fr_ipaddr_t::@1 ipaddr
int fr_packet_cmp(RADIUS_PACKET const *a, RADIUS_PACKET const *b)
Definition: packet.c:40
unsigned int code
Packet code (type).
Definition: libradius.h:155
void rbtree_delete(rbtree_t *tree, rbnode_t *z)
Definition: rbtree.c:488
bool fr_packet_list_socket_add(fr_packet_list_t *pl, int sockfd, int proto, fr_ipaddr_t *dst_ipaddr, uint16_t dst_port, void *ctx)
Definition: packet.c:356
int rbtree_walk(rbtree_t *tree, rb_order_t order, rb_walker_t compare, void *context)
Definition: rbtree.c:693
fr_ipaddr_t dst_ipaddr
Definition: packet.c:252
uint32_t num_outgoing
Definition: packet.c:245
#define MAX_SOCKETS
Definition: packet.c:266
bool fr_packet_list_id_free(fr_packet_list_t *pl, RADIUS_PACKET *request, bool yank)
Definition: packet.c:830
int(* rb_walker_t)(void *ctx, void *data)
Definition: libradius.h:530
int fr_packet_list_walk(fr_packet_list_t *pl, void *ctx, rb_walker_t callback)
Definition: packet.c:867
int if_index
Index of receiving interface.
Definition: libradius.h:148
RADIUS_PACKET * fr_tcp_recv(int sockfd, int flags)
Definition: tcp.c:32
bool fr_packet_list_yank(fr_packet_list_t *pl, RADIUS_PACKET *request)
Definition: packet.c:571
static int packet_entry_cmp(void const *one, void const *two)
Definition: packet.c:444
int fr_packet_list_fd_set(fr_packet_list_t *pl, fd_set *set)
Definition: packet.c:874
size_t data_len
Length of packet data.
Definition: libradius.h:161
bool rbtree_insert(rbtree_t *tree, void *data)
Definition: rbtree.c:329
#define ID_j
uint32_t num_outgoing
Definition: packet.c:278
#define SOCK2OFFSET(sockfd)
Definition: packet.c:268
void fr_strerror_printf(char const *,...) CC_HINT(format(printf
static rbtree_t * pl
Definition: process.c:53
char * fr_inet_ntop(char out[FR_IPADDR_STRLEN], size_t outlen, fr_ipaddr_t *addr)
Print the address portion of a fr_ipaddr_t.
Definition: inet.c:713
void fr_packet_list_free(fr_packet_list_t *pl)
Definition: packet.c:452
#define SOCKOFFSET_MASK
Definition: packet.c:267
int fr_socket(fr_ipaddr_t *ipaddr, uint16_t port)
Definition: packet.c:136
RADIUS_PACKET ** fr_packet_list_find_byreply(fr_packet_list_t *pl, RADIUS_PACKET *reply)
Definition: packet.c:514
int fr_is_inaddr_any(fr_ipaddr_t *ipaddr)
Determine if an address is the INADDR_ANY address for its address family.
Definition: packet.c:91
IPv4/6 prefix.
Definition: inet.h:41
#define ID_i
RADIUS_PACKET * fr_radius_recv(TALLOC_CTX *ctx, int fd, int flags)
Receive UDP client requests, and fill in the basics of a RADIUS_PACKET structure. ...
Definition: radius.c:1050
fr_packet_list_t * fr_packet_list_create(int alloc_id)
Definition: packet.c:464
#define RCSID(id)
Definition: build.h:135
RADIUS_PACKET * fr_packet_list_recv(fr_packet_list_t *pl, fd_set *set)
Definition: packet.c:901
bool fr_packet_list_insert(fr_packet_list_t *pl, RADIUS_PACKET **request_p)
Definition: packet.c:493
RADIUS_PACKET ** fr_packet_list_find(fr_packet_list_t *pl, RADIUS_PACKET *request)
Definition: packet.c:501
int fr_ipaddr_cmp(fr_ipaddr_t const *a, fr_ipaddr_t const *b)
Compare two ip addresses.
Definition: inet.c:1026
uint32_t fr_packet_list_num_incoming(fr_packet_list_t *pl)
Definition: packet.c:940
#define fr_exit(_x)
Definition: libradius.h:508
#define ID_k
#define is_radius_code(_x)
Definition: libradius.h:372
uint8_t id[32]
Definition: packet.c:261
int fr_ipaddr_to_sockaddr(fr_ipaddr_t const *ipaddr, uint16_t port, struct sockaddr_storage *sa, socklen_t *salen)
Definition: inet.c:1057
uint32_t rbtree_num_elements(rbtree_t *tree)
Definition: rbtree.c:727