Pulled fix from branch_1_1
[freeradius.git] / src / lib / packet.c
1 /*
2  * packet.c     Generic packet manipulation functions.
3  *
4  * Version:     $Id$
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 #include        <freeradius-devel/ident.h>
24 RCSID("$Id$")
25
26 #include        <freeradius-devel/libradius.h>
27
28 #ifdef WITH_UDPFROMTO
29 #include        <freeradius-devel/udpfromto.h>
30 #endif
31
32 /*
33  *      Take the key fields of a request packet, and convert it to a
34  *      hash.
35  */
36 uint32_t lrad_request_packet_hash(const RADIUS_PACKET *packet)
37 {
38         uint32_t hash;
39
40         if (packet->hash) return packet->hash;
41
42         hash = lrad_hash(&packet->sockfd, sizeof(packet->sockfd));
43         hash = lrad_hash_update(&packet->src_port, sizeof(packet->src_port),
44                                 hash);
45         hash = lrad_hash_update(&packet->dst_port,
46                                 sizeof(packet->dst_port), hash);
47         hash = lrad_hash_update(&packet->src_ipaddr.af,
48                                 sizeof(packet->src_ipaddr.af), hash);
49
50         /*
51          *      The caller ensures that src & dst AF are the same.
52          */
53         switch (packet->src_ipaddr.af) {
54         case AF_INET:
55                 hash = lrad_hash_update(&packet->src_ipaddr.ipaddr.ip4addr,
56                                         sizeof(packet->src_ipaddr.ipaddr.ip4addr),
57                                         hash);
58                 hash = lrad_hash_update(&packet->dst_ipaddr.ipaddr.ip4addr,
59                                         sizeof(packet->dst_ipaddr.ipaddr.ip4addr),
60                                         hash);
61                 break;
62         case AF_INET6:
63                 hash = lrad_hash_update(&packet->src_ipaddr.ipaddr.ip6addr,
64                                         sizeof(packet->src_ipaddr.ipaddr.ip6addr),
65                                         hash);
66                 hash = lrad_hash_update(&packet->dst_ipaddr.ipaddr.ip6addr,
67                                         sizeof(packet->dst_ipaddr.ipaddr.ip6addr),
68                                         hash);
69                 break;
70         default:
71                 break;
72         }
73
74         return lrad_hash_update(&packet->id, sizeof(packet->id), hash);
75 }
76
77
78 /*
79  *      Take the key fields of a reply packet, and convert it to a
80  *      hash.
81  *
82  *      i.e. take a reply packet, and find the hash of the request packet
83  *      that asked for the reply.  To do this, we hash the reverse fields
84  *      of the request.  e.g. where the request does (src, dst), we do
85  *      (dst, src)
86  */
87 uint32_t lrad_reply_packet_hash(const RADIUS_PACKET *packet)
88 {
89         uint32_t hash;
90
91         hash = lrad_hash(&packet->sockfd, sizeof(packet->sockfd));
92         hash = lrad_hash_update(&packet->id, sizeof(packet->id), hash);
93         hash = lrad_hash_update(&packet->src_port, sizeof(packet->src_port),
94                                 hash);
95         hash = lrad_hash_update(&packet->dst_port,
96                                 sizeof(packet->dst_port), hash);
97         hash = lrad_hash_update(&packet->src_ipaddr.af,
98                                 sizeof(packet->src_ipaddr.af), hash);
99
100         /*
101          *      The caller ensures that src & dst AF are the same.
102          */
103         switch (packet->src_ipaddr.af) {
104         case AF_INET:
105                 hash = lrad_hash_update(&packet->dst_ipaddr.ipaddr.ip4addr,
106                                         sizeof(packet->dst_ipaddr.ipaddr.ip4addr),
107                                         hash);
108                 hash = lrad_hash_update(&packet->src_ipaddr.ipaddr.ip4addr,
109                                         sizeof(packet->src_ipaddr.ipaddr.ip4addr),
110                                         hash);
111                 break;
112         case AF_INET6:
113                 hash = lrad_hash_update(&packet->dst_ipaddr.ipaddr.ip6addr,
114                                         sizeof(packet->dst_ipaddr.ipaddr.ip6addr),
115                                         hash);
116                 hash = lrad_hash_update(&packet->src_ipaddr.ipaddr.ip6addr,
117                                         sizeof(packet->src_ipaddr.ipaddr.ip6addr),
118                                         hash);
119                 break;
120         default:
121                 break;
122         }
123
124         return lrad_hash_update(&packet->id, sizeof(packet->id), hash);
125 }
126
127
128 /*
129  *      See if two packets are identical.
130  *
131  *      Note that we do NOT compare the authentication vectors.
132  *      That's because if the authentication vector is different,
133  *      it means that the NAS has given up on the earlier request.
134  */
135 int lrad_packet_cmp(const RADIUS_PACKET *a, const RADIUS_PACKET *b)
136 {
137         int rcode;
138
139         if (a->sockfd < b->sockfd) return -1;
140         if (a->sockfd > b->sockfd) return +1;
141
142         if (a->id < b->id) return -1;
143         if (a->id > b->id) return +1;
144
145         if (a->src_port < b->src_port) return -1;
146         if (a->src_port > b->src_port) return +1;
147
148         if (a->dst_port < b->dst_port) return -1;
149         if (a->dst_port > b->dst_port) return +1;
150
151         rcode = lrad_ipaddr_cmp(&a->dst_ipaddr, &b->dst_ipaddr);
152         if (rcode != 0) return rcode;
153         return lrad_ipaddr_cmp(&a->src_ipaddr, &b->src_ipaddr);
154 }
155
156
157 /*
158  *      Create a fake "request" from a reply, for later lookup.
159  */
160 void lrad_request_from_reply(RADIUS_PACKET *request,
161                              const RADIUS_PACKET *reply)
162 {
163         request->sockfd = reply->sockfd;
164         request->id = reply->id;
165         request->src_port = reply->dst_port;
166         request->dst_port = reply->src_port;
167         request->src_ipaddr = reply->dst_ipaddr;
168         request->dst_ipaddr = reply->src_ipaddr;
169 }
170
171
172 /*
173  *      Open a socket on the given IP and port.
174  */
175 int lrad_socket(lrad_ipaddr_t *ipaddr, int port)
176 {
177         int sockfd;
178         struct sockaddr_storage salocal;
179         socklen_t       salen;
180
181         if ((port < 0) || (port > 65535)) {
182                 librad_log("Port %d is out of allowed bounds", port);
183                 return -1;
184         }
185
186         sockfd = socket(ipaddr->af, SOCK_DGRAM, 0);
187         if (sockfd < 0) {
188                 return sockfd;
189         }
190
191 #ifdef WITH_UDPFROMTO
192         /*
193          *      Initialize udpfromto for all sockets.
194          */
195         if (udpfromto_init(sockfd) != 0) {
196                 close(sockfd);
197                 return -1;
198         }
199 #endif
200
201         memset(&salocal, 0, sizeof(salocal));
202         if (ipaddr->af == AF_INET) {
203                 struct sockaddr_in *sa;
204
205                 sa = (struct sockaddr_in *) &salocal;
206                 sa->sin_family = AF_INET;
207                 sa->sin_addr = ipaddr->ipaddr.ip4addr;
208                 sa->sin_port = htons((uint16_t) port);
209                 salen = sizeof(*sa);
210
211 #ifdef HAVE_STRUCT_SOCKADDR_IN6
212         } else if (ipaddr->af == AF_INET6) {
213                 struct sockaddr_in6 *sa;
214
215                 sa = (struct sockaddr_in6 *) &salocal;
216                 sa->sin6_family = AF_INET6;
217                 sa->sin6_addr = ipaddr->ipaddr.ip6addr;
218                 sa->sin6_port = htons((uint16_t) port);
219                 salen = sizeof(*sa);
220
221 #if 1
222                 /*
223                  *      Listening on '::' does NOT get you IPv4 to
224                  *      IPv6 mapping.  You've got to listen on an IPv4
225                  *      address, too.  This makes the rest of the server
226                  *      design a little simpler.
227                  */
228 #ifdef IPV6_V6ONLY
229
230                 if (IN6_IS_ADDR_UNSPECIFIED(&ipaddr->ipaddr.ip6addr)) {
231                         int on = 1;
232
233                         setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY,
234                                    (char *)&on, sizeof(on));
235                 }
236 #endif /* IPV6_V6ONLY */
237 #endif
238 #endif /* HAVE_STRUCT_SOCKADDR_IN6 */
239         } else {
240                 return sockfd;  /* don't bind it */
241         }
242
243         if (bind(sockfd, (struct sockaddr *) &salocal, salen) < 0) {
244                 close(sockfd);
245                 return -1;
246         }
247
248         return sockfd;
249 }
250
251
252 /*
253  *      We need to keep track of the socket & it's IP/port.
254  */
255 typedef struct lrad_packet_socket_t {
256         int             sockfd;
257
258         int             num_outgoing;
259
260         int             offset; /* 0..31 */
261         int             inaddr_any;
262         lrad_ipaddr_t   ipaddr;
263         int             port;
264 } lrad_packet_socket_t;
265
266
267 #define FNV_MAGIC_PRIME (0x01000193)
268 #define MAX_SOCKETS (32)
269 #define SOCKOFFSET_MASK (MAX_SOCKETS - 1)
270 #define SOCK2OFFSET(sockfd) ((sockfd * FNV_MAGIC_PRIME) & SOCKOFFSET_MASK)
271
272 #define MAX_QUEUES (8)
273
274 /*
275  *      Structure defining a list of packets (incoming or outgoing)
276  *      that should be managed.
277  */
278 struct lrad_packet_list_t {
279         lrad_hash_table_t *ht;
280
281         lrad_hash_table_t *dst2id_ht;
282
283         int             alloc_id;
284         int             num_outgoing;
285
286         uint32_t mask;
287         int last_recv;
288         lrad_packet_socket_t sockets[MAX_SOCKETS];
289 };
290
291
292 /*
293  *      Ugh.  Doing this on every sent/received packet is not nice.
294  */
295 static lrad_packet_socket_t *lrad_socket_find(lrad_packet_list_t *pl,
296                                              int sockfd)
297 {
298         int i, start;
299
300         i = start = SOCK2OFFSET(sockfd);
301
302         do {                    /* make this hack slightly more efficient */
303                 if (pl->sockets[i].sockfd == sockfd) return &pl->sockets[i];
304
305                 i = (i + 1) & SOCKOFFSET_MASK;
306         } while (i != start);
307
308         return NULL;
309 }
310
311 int lrad_packet_list_socket_remove(lrad_packet_list_t *pl, int sockfd)
312 {
313         lrad_packet_socket_t *ps;
314
315         if (!pl) return 0;
316
317         ps = lrad_socket_find(pl, sockfd);
318         if (!ps) return 0;
319
320         /*
321          *      FIXME: Allow the caller forcibly discard these?
322          */
323         if (ps->num_outgoing != 0) return 0;
324
325         ps->sockfd = -1;
326         pl->mask &= ~(1 << ps->offset);
327
328
329         return 1;
330 }
331
332 int lrad_packet_list_socket_add(lrad_packet_list_t *pl, int sockfd)
333 {
334         int i, start;
335         struct sockaddr_storage src;
336         socklen_t               sizeof_src = sizeof(src);
337         lrad_packet_socket_t    *ps;
338
339         if (!pl) return 0;
340
341         ps = NULL;
342         i = start = SOCK2OFFSET(sockfd);
343
344         do {
345                 if (pl->sockets[i].sockfd == -1) {
346                         ps =  &pl->sockets[i];
347                         start = i;
348                         break;
349                 }
350
351                 i = (i + 1) & SOCKOFFSET_MASK;
352         } while (i != start);
353
354         if (!ps) {
355                 return 0;
356         }
357
358         memset(ps, 0, sizeof(*ps));
359         ps->sockfd = sockfd;
360         ps->offset = start;
361
362         /*
363          *      Get address family, etc. first, so we know if we
364          *      need to do udpfromto.
365          *
366          *      FIXME: udpfromto also does this, but it's not
367          *      a critical problem.
368          */
369         memset(&src, 0, sizeof_src);
370         if (getsockname(sockfd, (struct sockaddr *) &src,
371                         &sizeof_src) < 0) {
372                 return 0;
373         }
374
375         /*
376          *      Grab IP addresses & ports from the sockaddr.
377          */
378         ps->ipaddr.af = src.ss_family;
379         if (src.ss_family == AF_INET) {
380                 struct sockaddr_in      *s4;
381
382                 s4 = (struct sockaddr_in *)&src;
383                 ps->ipaddr.ipaddr.ip4addr = s4->sin_addr;
384                 ps->port = ntohs(s4->sin_port);
385
386                 if (ps->ipaddr.ipaddr.ip4addr.s_addr == INADDR_ANY) {
387                         ps->inaddr_any = 1;
388                 }
389
390 #ifdef HAVE_STRUCT_SOCKADDR_IN6
391         } else if (src.ss_family == AF_INET6) {
392                 struct sockaddr_in6     *s6;
393
394                 s6 = (struct sockaddr_in6 *)&src;
395                 ps->ipaddr.ipaddr.ip6addr = s6->sin6_addr;
396                 ps->port = ntohs(s6->sin6_port);
397
398                 if (IN6_IS_ADDR_UNSPECIFIED(&ps->ipaddr.ipaddr.ip6addr)) {
399                         ps->inaddr_any = 1;
400                 }
401 #endif
402         } else {
403                 return 0;
404         }
405
406         pl->mask |= (1 << ps->offset);
407         return 1;
408 }
409
410 static uint32_t packet_entry_hash(const void *data)
411 {
412         return lrad_request_packet_hash(*(const RADIUS_PACKET * const *) data);
413 }
414
415 static int packet_entry_cmp(const void *one, const void *two)
416 {
417         const RADIUS_PACKET * const *a = one;
418         const RADIUS_PACKET * const *b = two;
419
420         return lrad_packet_cmp(*a, *b);
421 }
422
423 /*
424  *      A particular socket can have 256 RADIUS ID's outstanding to
425  *      any one destination IP/port.  So we have a structure that
426  *      manages destination IP & port, and has an array of 256 ID's.
427  *
428  *      The only magic here is that we map the socket number (0..256)
429  *      into an "internal" socket number 0..31, that we use to set
430  *      bits in the ID array.  If a bit is 1, then that ID is in use
431  *      for that socket, and the request MUST be in the packet hash!
432  *
433  *      Note that as a minor memory leak, we don't have an API to free
434  *      this structure, except when we discard the whole packet list.
435  *      This means that if destinations are added and removed, they
436  *      won't be removed from this tree.
437  */
438 typedef struct lrad_packet_dst2id_t {
439         lrad_ipaddr_t   dst_ipaddr;
440         int             dst_port;
441         uint32_t        id[1];  /* really id[256] */
442 } lrad_packet_dst2id_t;
443
444
445 static uint32_t packet_dst2id_hash(const void *data)
446 {
447         uint32_t hash;
448         const lrad_packet_dst2id_t *pd = data;
449
450         hash = lrad_hash(&pd->dst_port, sizeof(pd->dst_port));
451
452         switch (pd->dst_ipaddr.af) {
453         case AF_INET:
454                 hash = lrad_hash_update(&pd->dst_ipaddr.ipaddr.ip4addr,
455                                         sizeof(pd->dst_ipaddr.ipaddr.ip4addr),
456                                         hash);
457                 break;
458         case AF_INET6:
459                 hash = lrad_hash_update(&pd->dst_ipaddr.ipaddr.ip6addr,
460                                         sizeof(pd->dst_ipaddr.ipaddr.ip6addr),
461                                         hash);
462                 break;
463         default:
464                 break;
465         }
466
467         return hash;
468 }
469
470 static int packet_dst2id_cmp(const void *one, const void *two)
471 {
472         const lrad_packet_dst2id_t *a = one;
473         const lrad_packet_dst2id_t *b = two;
474
475         if (a->dst_port < b->dst_port) return -1;
476         if (a->dst_port > b->dst_port) return +1;
477
478         return lrad_ipaddr_cmp(&a->dst_ipaddr, &b->dst_ipaddr);
479 }
480
481 static void packet_dst2id_free(void *data)
482 {
483         free(data);
484 }
485
486
487 void lrad_packet_list_free(lrad_packet_list_t *pl)
488 {
489         if (!pl) return;
490
491         lrad_hash_table_free(pl->ht);
492         lrad_hash_table_free(pl->dst2id_ht);
493         free(pl);
494 }
495
496
497 /*
498  *      Caller is responsible for managing the packet entries.
499  */
500 lrad_packet_list_t *lrad_packet_list_create(int alloc_id)
501 {
502         int i;
503         lrad_packet_list_t      *pl;
504
505         pl = malloc(sizeof(*pl));
506         if (!pl) return NULL;
507         memset(pl, 0, sizeof(*pl));
508
509         pl->ht = lrad_hash_table_create(packet_entry_hash,
510                                         packet_entry_cmp,
511                                         NULL);
512         if (!pl->ht) {
513                 lrad_packet_list_free(pl);
514                 return NULL;
515         }
516
517         for (i = 0; i < MAX_SOCKETS; i++) {
518                 pl->sockets[i].sockfd = -1;
519         }
520
521         if (alloc_id) {
522                 pl->alloc_id = 1;
523
524                 pl->dst2id_ht = lrad_hash_table_create(packet_dst2id_hash,
525                                                        packet_dst2id_cmp,
526                                                        packet_dst2id_free);
527                 if (!pl->dst2id_ht) {
528                         lrad_packet_list_free(pl);
529                         return NULL;
530                 }
531         }
532
533         return pl;
534 }
535
536
537 /*
538  *      If pl->alloc_id is set, then lrad_packet_list_id_alloc() MUST
539  *      be called before inserting the packet into the list!
540  */
541 int lrad_packet_list_insert(lrad_packet_list_t *pl,
542                             RADIUS_PACKET **request_p)
543 {
544         if (!pl || !request_p || !*request_p) return 0;
545
546         (*request_p)->hash = lrad_request_packet_hash(*request_p);
547
548         return lrad_hash_table_insert(pl->ht, request_p);
549 }
550
551 RADIUS_PACKET **lrad_packet_list_find(lrad_packet_list_t *pl,
552                                       RADIUS_PACKET *request)
553 {
554         if (!pl || !request) return 0;
555
556         return lrad_hash_table_finddata(pl->ht, &request);
557 }
558
559
560 /*
561  *      This presumes that the reply has dst_ipaddr && dst_port set up
562  *      correctly (i.e. real IP, or "*").
563  */
564 RADIUS_PACKET **lrad_packet_list_find_byreply(lrad_packet_list_t *pl,
565                                               RADIUS_PACKET *reply)
566 {
567         RADIUS_PACKET my_request, *request;
568         lrad_packet_socket_t *ps;
569
570         if (!pl || !reply) return NULL;
571
572         ps = lrad_socket_find(pl, reply->sockfd);
573         if (!ps) return NULL;
574
575         /*
576          *      Initialize request from reply, AND from the source
577          *      IP & port of this socket.  The client may have bound
578          *      the socket to 0, in which case it's some random port,
579          *      that is NOT in the original request->src_port.
580          */
581         my_request.sockfd = reply->sockfd;
582         my_request.id = reply->id;
583
584         if (ps->inaddr_any) {
585                 my_request.src_ipaddr = ps->ipaddr;
586         } else {
587                 my_request.src_ipaddr = reply->dst_ipaddr;
588         }
589         my_request.src_port = ps->port;;
590
591         my_request.dst_ipaddr = reply->src_ipaddr;
592         my_request.dst_port = reply->src_port;
593         my_request.hash = 0;
594
595         request = &my_request;
596
597         return lrad_hash_table_finddata(pl->ht, &request);
598 }
599
600
601 RADIUS_PACKET **lrad_packet_list_yank(lrad_packet_list_t *pl,
602                                       RADIUS_PACKET *request)
603 {
604         if (!pl || !request) return NULL;
605
606         return lrad_hash_table_yank(pl->ht, &request);
607 }
608
609 int lrad_packet_list_num_elements(lrad_packet_list_t *pl)
610 {
611         if (!pl) return 0;
612
613         return lrad_hash_table_num_elements(pl->ht);
614 }
615
616
617 /*
618  *      1 == ID was allocated & assigned
619  *      0 == error allocating memory
620  *      -1 == all ID's are used, caller should open a new socket.
621  *
622  *      Note that this ALSO assigns a socket to use, and updates
623  *      packet->request->src_ipaddr && packet->request->src_port
624  *
625  *      In multi-threaded systems, the calls to id_alloc && id_free
626  *      should be protected by a mutex.  This does NOT have to be
627  *      the same mutex as the one protecting the insert/find/yank
628  *      calls!
629  */
630 int lrad_packet_list_id_alloc(lrad_packet_list_t *pl,
631                               RADIUS_PACKET *request)
632 {
633         int i, id, start;
634         uint32_t free_mask;
635         lrad_packet_dst2id_t my_pd, *pd;
636         lrad_packet_socket_t *ps;
637
638         if (!pl || !pl->alloc_id || !request) return 0;
639
640         my_pd.dst_ipaddr = request->dst_ipaddr;
641         my_pd.dst_port = request->dst_port;
642
643         pd = lrad_hash_table_finddata(pl->dst2id_ht, &my_pd);
644         if (!pd) {
645                 pd = malloc(sizeof(*pd) + 255 * sizeof(pd->id[0]));
646                 if (!pd) return 0;
647
648                 memset(pd, 0, sizeof(*pd) + 255 * sizeof(pd->id[0]));
649
650                 pd->dst_ipaddr = request->dst_ipaddr;
651                 pd->dst_port = request->dst_port;
652
653                 if (!lrad_hash_table_insert(pl->dst2id_ht, pd)) {
654                         free(pd);
655                         return 0;
656                 }
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         id = start = (int) lrad_rand() & 0xff;
671
672         while (pd->id[id] == pl->mask) { /* all sockets are using this ID */
673                 id++;
674                 id &= 0xff;
675                 if (id == start) return 0;
676         }
677
678         free_mask = ~((~pd->id[id]) & pl->mask);
679
680         start = -1;
681         for (i = 0; i < MAX_SOCKETS; i++) {
682                 if (pl->sockets[i].sockfd == -1) continue; /* paranoia */
683
684                 if ((free_mask & (1 << i)) == 0) {
685                         start = i;
686                         break;
687                 }
688         }
689
690         if (start < 0) return 0; /* bad error */
691
692         pd->id[id] |= (1 << start);
693         ps = &pl->sockets[start];
694
695         ps->num_outgoing++;
696         pl->num_outgoing++;
697
698         /*
699          *      Set the ID, source IP, and source port.
700          */
701         request->id = id;
702
703         request->sockfd = ps->sockfd;
704         request->src_ipaddr = ps->ipaddr;
705         request->src_port = ps->port;
706
707         return 1;
708 }
709
710 /*
711  *      Should be called AFTER yanking it from the list, so that
712  *      any newly inserted entries don't collide with this one.
713  */
714 int lrad_packet_list_id_free(lrad_packet_list_t *pl,
715                              RADIUS_PACKET *request)
716 {
717         lrad_packet_socket_t *ps;
718         lrad_packet_dst2id_t my_pd, *pd;
719
720         if (!pl || !request) return 0;
721
722         ps = lrad_socket_find(pl, request->sockfd);
723         if (!ps) return 0;
724
725         my_pd.dst_ipaddr = request->dst_ipaddr;
726         my_pd.dst_port = request->dst_port;
727
728         pd = lrad_hash_table_finddata(pl->dst2id_ht, &my_pd);
729         if (!pd) return 0;
730
731         pd->id[request->id] &= ~(1 << ps->offset);
732         request->hash = 0;      /* invalidate the cached hash */
733
734         ps->num_outgoing--;
735         pl->num_outgoing--;
736
737         return 1;
738 }
739
740 int lrad_packet_list_walk(lrad_packet_list_t *pl, void *ctx,
741                           lrad_hash_table_walk_t callback)
742 {
743         if (!pl || !callback) return 0;
744
745         return lrad_hash_table_walk(pl->ht, callback, ctx);
746 }
747
748 int lrad_packet_list_fd_set(lrad_packet_list_t *pl, fd_set *set)
749 {
750         int i, maxfd;
751
752         if (!pl || !set) return 0;
753
754         maxfd = -1;
755
756         for (i = 0; i < MAX_SOCKETS; i++) {
757                 if (pl->sockets[i].sockfd == -1) continue;
758                 FD_SET(pl->sockets[i].sockfd, set);
759                 if (pl->sockets[i].sockfd > maxfd) {
760                         maxfd = pl->sockets[i].sockfd;
761                 }
762         }
763
764         if (maxfd < 0) return -1;
765
766         return maxfd + 1;
767 }
768
769 /*
770  *      Round-robins the receivers, without priority.
771  *
772  *      FIXME: Add sockfd, if -1, do round-robin, else do sockfd
773  *              IF in fdset.
774  */
775 RADIUS_PACKET *lrad_packet_list_recv(lrad_packet_list_t *pl, fd_set *set)
776 {
777         int start;
778         RADIUS_PACKET *packet;
779
780         if (!pl || !set) return NULL;
781
782         start = pl->last_recv;
783         do {
784                 start++;
785                 start &= SOCKOFFSET_MASK;
786
787                 if (pl->sockets[start].sockfd == -1) continue;
788
789                 if (!FD_ISSET(pl->sockets[start].sockfd, set)) continue;
790
791                 packet = rad_recv(pl->sockets[start].sockfd);
792                 if (!packet) continue;
793
794                 /*
795                  *      Call lrad_packet_list_find_byreply().  If it
796                  *      doesn't find anything, discard the reply.
797                  */
798
799                 pl->last_recv = start;
800                 return packet;
801         } while (start != pl->last_recv);
802
803         return NULL;
804 }
805
806 int lrad_packet_list_num_incoming(lrad_packet_list_t *pl)
807 {
808         int num_elements;
809
810         if (!pl) return 0;
811
812         num_elements = lrad_hash_table_num_elements(pl->ht);
813         if (num_elements < pl->num_outgoing) return 0; /* panic! */
814
815         return num_elements - pl->num_outgoing;
816 }
817
818 int lrad_packet_list_num_outgoing(lrad_packet_list_t *pl)
819 {
820         if (!pl) return 0;
821
822         return pl->num_outgoing;
823 }