2 * packet.c Generic packet manipulation functions.
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.
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.
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
20 * Copyright 2000-2006 The FreeRADIUS server project
23 #include <freeradius-devel/ident.h>
26 #include <freeradius-devel/autoconf.h>
27 #include <freeradius-devel/libradius.h>
33 * Take the key fields of a request packet, and convert it to a
36 uint32_t lrad_request_packet_hash(const RADIUS_PACKET *packet)
40 if (packet->hash) return packet->hash;
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),
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);
52 * The caller ensures that src & dst AF are the same.
54 switch (packet->src_ipaddr.af) {
56 hash = lrad_hash_update(&packet->src_ipaddr.ipaddr.ip4addr,
57 sizeof(packet->src_ipaddr.ipaddr.ip4addr),
59 hash = lrad_hash_update(&packet->dst_ipaddr.ipaddr.ip4addr,
60 sizeof(packet->dst_ipaddr.ipaddr.ip4addr),
64 hash = lrad_hash_update(&packet->src_ipaddr.ipaddr.ip6addr,
65 sizeof(packet->src_ipaddr.ipaddr.ip6addr),
67 hash = lrad_hash_update(&packet->dst_ipaddr.ipaddr.ip6addr,
68 sizeof(packet->dst_ipaddr.ipaddr.ip6addr),
75 return lrad_hash_update(&packet->id, sizeof(packet->id), hash);
80 * Take the key fields of a reply packet, and convert it to a
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
88 uint32_t lrad_reply_packet_hash(const RADIUS_PACKET *packet)
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),
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);
102 * The caller ensures that src & dst AF are the same.
104 switch (packet->src_ipaddr.af) {
106 hash = lrad_hash_update(&packet->dst_ipaddr.ipaddr.ip4addr,
107 sizeof(packet->dst_ipaddr.ipaddr.ip4addr),
109 hash = lrad_hash_update(&packet->src_ipaddr.ipaddr.ip4addr,
110 sizeof(packet->src_ipaddr.ipaddr.ip4addr),
114 hash = lrad_hash_update(&packet->dst_ipaddr.ipaddr.ip6addr,
115 sizeof(packet->dst_ipaddr.ipaddr.ip6addr),
117 hash = lrad_hash_update(&packet->src_ipaddr.ipaddr.ip6addr,
118 sizeof(packet->src_ipaddr.ipaddr.ip6addr),
125 return lrad_hash_update(&packet->id, sizeof(packet->id), hash);
129 static int lrad_ipaddr_cmp(const lrad_ipaddr_t *a, const lrad_ipaddr_t *b)
131 if (a->af < b->af) return -1;
132 if (a->af > b->af) return +1;
136 return memcmp(&a->ipaddr.ip4addr,
138 sizeof(a->ipaddr.ip4addr));
141 return memcmp(&a->ipaddr.ip6addr,
143 sizeof(a->ipaddr.ip6addr));
154 * See if two packets are identical.
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.
160 int lrad_packet_cmp(const RADIUS_PACKET *a, const RADIUS_PACKET *b)
164 if (a->sockfd < b->sockfd) return -1;
165 if (a->sockfd > b->sockfd) return +1;
167 if (a->id < b->id) return -1;
168 if (a->id > b->id) return +1;
170 if (a->src_port < b->src_port) return -1;
171 if (a->src_port > b->src_port) return +1;
173 if (a->dst_port < b->dst_port) return -1;
174 if (a->dst_port > b->dst_port) return +1;
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);
183 * Create a fake "request" from a reply, for later lookup.
185 void lrad_request_from_reply(RADIUS_PACKET *request,
186 const RADIUS_PACKET *reply)
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;
198 * Open a socket on the given IP and port.
200 int lrad_socket(lrad_ipaddr_t *ipaddr, int port)
203 struct sockaddr_storage salocal;
206 if ((port < 0) || (port > 65535)) {
207 librad_log("Port %d is out of allowed bounds", port);
211 sockfd = socket(ipaddr->af, SOCK_DGRAM, 0);
216 #ifdef WITH_UDPFROMTO
218 * Initialize udpfromto for all sockets.
220 if (udpfromto_init(sockfd) != 0) {
226 memset(&salocal, 0, sizeof(salocal));
227 if (ipaddr->af == AF_INET) {
228 struct sockaddr_in *sa;
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);
236 #ifdef HAVE_STRUCT_SOCKADDR_IN6
237 } else if (ipaddr->af == AF_INET6) {
238 struct sockaddr_in6 *sa;
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);
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.
255 if (IN6_IS_ADDR_UNSPECIFIED(&ipaddr->ipaddr.ip6addr)) {
258 setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY,
259 (char *)&on, sizeof(on));
261 #endif /* IPV6_V6ONLY */
263 #endif /* HAVE_STRUCT_SOCKADDR_IN6 */
265 return sockfd; /* don't bind it */
268 if (bind(sockfd, (struct sockaddr *) &salocal, salen) < 0) {
278 * We need to keep track of the socket & it's IP/port.
280 typedef struct lrad_packet_socket_t {
285 int offset; /* 0..31 */
287 lrad_ipaddr_t ipaddr;
289 } lrad_packet_socket_t;
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)
297 #define MAX_QUEUES (8)
300 * Structure defining a list of packets (incoming or outgoing)
301 * that should be managed.
303 struct lrad_packet_list_t {
304 lrad_hash_table_t *ht;
306 lrad_hash_table_t *dst2id_ht;
313 lrad_packet_socket_t sockets[MAX_SOCKETS];
318 * Ugh. Doing this on every sent/received packet is not nice.
320 static lrad_packet_socket_t *lrad_socket_find(lrad_packet_list_t *pl,
325 i = start = SOCK2OFFSET(sockfd);
327 do { /* make this hack slightly more efficient */
328 if (pl->sockets[i].sockfd == sockfd) return &pl->sockets[i];
330 i = (i + 1) & SOCKOFFSET_MASK;
331 } while (i != start);
336 int lrad_packet_list_socket_remove(lrad_packet_list_t *pl, int sockfd)
338 lrad_packet_socket_t *ps;
342 ps = lrad_socket_find(pl, sockfd);
346 * FIXME: Allow the caller forcibly discard these?
348 if (ps->num_outgoing != 0) return 0;
351 pl->mask &= ~(1 << ps->offset);
357 int lrad_packet_list_socket_add(lrad_packet_list_t *pl, int sockfd)
360 struct sockaddr_storage src;
361 socklen_t sizeof_src = sizeof(src);
362 lrad_packet_socket_t *ps;
367 i = start = SOCK2OFFSET(sockfd);
370 if (pl->sockets[i].sockfd == -1) {
371 ps = &pl->sockets[i];
376 i = (i + 1) & SOCKOFFSET_MASK;
377 } while (i != start);
383 memset(ps, 0, sizeof(*ps));
388 * Get address family, etc. first, so we know if we
389 * need to do udpfromto.
391 * FIXME: udpfromto also does this, but it's not
392 * a critical problem.
394 memset(&src, 0, sizeof_src);
395 if (getsockname(sockfd, (struct sockaddr *) &src,
401 * Grab IP addresses & ports from the sockaddr.
403 ps->ipaddr.af = src.ss_family;
404 if (src.ss_family == AF_INET) {
405 struct sockaddr_in *s4;
407 s4 = (struct sockaddr_in *)&src;
408 ps->ipaddr.ipaddr.ip4addr = s4->sin_addr;
409 ps->port = ntohs(s4->sin_port);
411 if (ps->ipaddr.ipaddr.ip4addr.s_addr == INADDR_ANY) {
415 #ifdef HAVE_STRUCT_SOCKADDR_IN6
416 } else if (src.ss_family == AF_INET6) {
417 struct sockaddr_in6 *s6;
419 s6 = (struct sockaddr_in6 *)&src;
420 ps->ipaddr.ipaddr.ip6addr = s6->sin6_addr;
421 ps->port = ntohs(s6->sin6_port);
423 if (IN6_IS_ADDR_UNSPECIFIED(&ps->ipaddr.ipaddr.ip6addr)) {
431 pl->mask |= (1 << ps->offset);
435 static uint32_t packet_entry_hash(const void *data)
437 return lrad_request_packet_hash(*(const RADIUS_PACKET * const *) data);
440 static int packet_entry_cmp(const void *one, const void *two)
442 const RADIUS_PACKET * const *a = one;
443 const RADIUS_PACKET * const *b = two;
445 return lrad_packet_cmp(*a, *b);
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.
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!
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.
463 typedef struct lrad_packet_dst2id_t {
464 lrad_ipaddr_t dst_ipaddr;
466 uint32_t id[1]; /* really id[256] */
467 } lrad_packet_dst2id_t;
470 static uint32_t packet_dst2id_hash(const void *data)
473 const lrad_packet_dst2id_t *pd = data;
475 hash = lrad_hash(&pd->dst_port, sizeof(pd->dst_port));
477 switch (pd->dst_ipaddr.af) {
479 hash = lrad_hash_update(&pd->dst_ipaddr.ipaddr.ip4addr,
480 sizeof(pd->dst_ipaddr.ipaddr.ip4addr),
484 hash = lrad_hash_update(&pd->dst_ipaddr.ipaddr.ip6addr,
485 sizeof(pd->dst_ipaddr.ipaddr.ip6addr),
495 static int packet_dst2id_cmp(const void *one, const void *two)
497 const lrad_packet_dst2id_t *a = one;
498 const lrad_packet_dst2id_t *b = two;
500 if (a->dst_port < b->dst_port) return -1;
501 if (a->dst_port > b->dst_port) return +1;
503 return lrad_ipaddr_cmp(&a->dst_ipaddr, &b->dst_ipaddr);
506 static void packet_dst2id_free(void *data)
508 lrad_packet_dst2id_t *pd = data;
515 void lrad_packet_list_free(lrad_packet_list_t *pl)
519 if (pl->ht) lrad_hash_table_free(pl->ht);
520 if (pl->dst2id_ht) lrad_hash_table_free(pl->dst2id_ht);
525 * Caller is responsible for managing the packet entries.
527 lrad_packet_list_t *lrad_packet_list_create(int alloc_id)
530 lrad_packet_list_t *pl;
532 pl = malloc(sizeof(*pl));
533 if (!pl) return NULL;
534 memset(pl, 0, sizeof(*pl));
536 pl->ht = lrad_hash_table_create(packet_entry_hash,
540 lrad_packet_list_free(pl);
544 for (i = 0; i < MAX_SOCKETS; i++) {
545 pl->sockets[i].sockfd = -1;
551 pl->dst2id_ht = lrad_hash_table_create(packet_dst2id_hash,
554 if (!pl->dst2id_ht) {
555 lrad_packet_list_free(pl);
565 * If pl->alloc_id is set, then lrad_packet_list_id_alloc() MUST
566 * be called before inserting the packet into the list!
568 int lrad_packet_list_insert(lrad_packet_list_t *pl,
569 RADIUS_PACKET **request_p)
571 if (!pl || !request_p || !*request_p) return 0;
573 (*request_p)->hash = lrad_request_packet_hash(*request_p);
575 return lrad_hash_table_insert(pl->ht, request_p);
578 RADIUS_PACKET **lrad_packet_list_find(lrad_packet_list_t *pl,
579 RADIUS_PACKET *request)
581 if (!pl || !request) return 0;
583 return lrad_hash_table_finddata(pl->ht, &request);
588 * This presumes that the reply has dst_ipaddr && dst_port set up
589 * correctly (i.e. real IP, or "*").
591 RADIUS_PACKET **lrad_packet_list_find_byreply(lrad_packet_list_t *pl,
592 RADIUS_PACKET *reply)
594 RADIUS_PACKET my_request, *request;
595 lrad_packet_socket_t *ps;
597 if (!pl || !reply) return NULL;
599 ps = lrad_socket_find(pl, reply->sockfd);
600 if (!ps) return NULL;
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.
608 my_request.sockfd = reply->sockfd;
609 my_request.id = reply->id;
611 if (ps->inaddr_any) {
612 my_request.src_ipaddr = ps->ipaddr;
614 my_request.src_ipaddr = reply->dst_ipaddr;
616 my_request.src_port = ps->port;;
618 my_request.dst_ipaddr = reply->src_ipaddr;
619 my_request.dst_port = reply->src_port;
622 request = &my_request;
624 return lrad_hash_table_finddata(pl->ht, &request);
628 RADIUS_PACKET **lrad_packet_list_yank(lrad_packet_list_t *pl,
629 RADIUS_PACKET *request)
631 if (!pl || !request) return NULL;
633 return lrad_hash_table_yank(pl->ht, &request);
636 int lrad_packet_list_num_elements(lrad_packet_list_t *pl)
640 return lrad_hash_table_num_elements(pl->ht);
645 * 1 == ID was allocated & assigned
646 * 0 == error allocating memory
647 * -1 == all ID's are used, caller should open a new socket.
649 * Note that this ALSO assigns a socket to use, and updates
650 * packet->request->src_ipaddr && packet->request->src_port
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
657 int lrad_packet_list_id_alloc(lrad_packet_list_t *pl,
658 RADIUS_PACKET *request)
662 lrad_packet_dst2id_t my_pd, *pd;
663 lrad_packet_socket_t *ps;
665 if (!pl || !pl->alloc_id || !request) return 0;
667 my_pd.dst_ipaddr = request->dst_ipaddr;
668 my_pd.dst_port = request->dst_port;
670 pd = lrad_hash_table_finddata(pl->dst2id_ht, &my_pd);
672 pd = malloc(sizeof(*pd) + 255 * sizeof(pd->id[0]));
675 memcpy(pd, &my_pd, sizeof(*pd) + 255 * sizeof(pd->id[0]));
676 memset(pd->id, 0, 256 * sizeof(pd->id[0]));
678 if (!lrad_hash_table_insert(pl->dst2id_ht, pd)) {
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.
691 id = start = (int) lrad_rand() & 0xff;
693 while (pd->id[id] == pl->mask) { /* all sockets are using this ID */
696 if (id == start) return -1;
699 free_mask = ~((~pd->id[id]) & pl->mask);
702 for (i = 0; i < MAX_SOCKETS; i++) {
703 if (pl->sockets[i].sockfd == -1) continue; /* paranoia */
705 if ((free_mask & (1 << i)) == 0) {
711 if (start < 0) return 0; /* bad error */
713 pd->id[id] |= (1 << start);
714 ps = &pl->sockets[start];
720 * Set the ID, source IP, and source port.
723 request->sockfd = ps->sockfd;
724 request->src_ipaddr = ps->ipaddr;
725 request->src_port = ps->port;
731 * Should be called AFTER yanking it from the list, so that
732 * any newly inserted entries don't collide with this one.
734 int lrad_packet_list_id_free(lrad_packet_list_t *pl,
735 RADIUS_PACKET *request)
737 lrad_packet_socket_t *ps;
738 lrad_packet_dst2id_t my_pd, *pd;
740 if (!pl || !request) return 0;
742 ps = lrad_socket_find(pl, request->sockfd);
745 my_pd.dst_ipaddr = request->dst_ipaddr;
746 my_pd.dst_port = request->dst_port;
748 pd = lrad_hash_table_finddata(pl->dst2id_ht, &my_pd);
751 pd->id[request->id] &= ~(1 << ps->offset);
752 request->hash = 0; /* invalidate the cached hash */
760 int lrad_packet_list_walk(lrad_packet_list_t *pl, void *ctx,
761 lrad_hash_table_walk_t callback)
763 if (!pl || !callback) return 0;
765 return lrad_hash_table_walk(pl->ht, callback, ctx);
768 int lrad_packet_list_fd_set(lrad_packet_list_t *pl, fd_set *set)
772 if (!pl || !set) return 0;
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;
784 if (maxfd < 0) return -1;
790 * Round-robins the receivers, without priority.
792 * FIXME: Add sockfd, if -1, do round-robin, else do sockfd
795 RADIUS_PACKET *lrad_packet_list_recv(lrad_packet_list_t *pl, fd_set *set)
798 RADIUS_PACKET *packet;
800 if (!pl || !set) return NULL;
802 start = pl->last_recv;
805 start &= SOCKOFFSET_MASK;
807 if (pl->sockets[start].sockfd == -1) continue;
809 if (!FD_ISSET(pl->sockets[start].sockfd, set)) continue;
811 packet = rad_recv(pl->sockets[start].sockfd);
812 if (!packet) continue;
815 * Call lrad_packet_list_find_byreply(). If it
816 * doesn't find anything, discard the reply.
819 pl->last_recv = start;
821 } while (start != pl->last_recv);
826 int lrad_packet_list_num_incoming(lrad_packet_list_t *pl)
832 num_elements = lrad_hash_table_num_elements(pl->ht);
833 if (num_elements < pl->num_outgoing) return 0; /* panic! */
835 return num_elements - pl->num_outgoing;
838 int lrad_packet_list_num_outgoing(lrad_packet_list_t *pl)
842 return pl->num_outgoing;