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