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