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/libradius.h>
29 #include <freeradius-devel/udpfromto.h>
35 * Take the key fields of a request packet, and convert it to a
38 uint32_t fr_request_packet_hash(const RADIUS_PACKET *packet)
42 if (packet->hash) return packet->hash;
44 hash = fr_hash(&packet->sockfd, sizeof(packet->sockfd));
45 hash = fr_hash_update(&packet->src_port, sizeof(packet->src_port),
47 hash = fr_hash_update(&packet->dst_port,
48 sizeof(packet->dst_port), hash);
49 hash = fr_hash_update(&packet->src_ipaddr.af,
50 sizeof(packet->src_ipaddr.af), hash);
53 * The caller ensures that src & dst AF are the same.
55 switch (packet->src_ipaddr.af) {
57 hash = fr_hash_update(&packet->src_ipaddr.ipaddr.ip4addr,
58 sizeof(packet->src_ipaddr.ipaddr.ip4addr),
60 hash = fr_hash_update(&packet->dst_ipaddr.ipaddr.ip4addr,
61 sizeof(packet->dst_ipaddr.ipaddr.ip4addr),
65 hash = fr_hash_update(&packet->src_ipaddr.ipaddr.ip6addr,
66 sizeof(packet->src_ipaddr.ipaddr.ip6addr),
68 hash = fr_hash_update(&packet->dst_ipaddr.ipaddr.ip6addr,
69 sizeof(packet->dst_ipaddr.ipaddr.ip6addr),
76 return fr_hash_update(&packet->id, sizeof(packet->id), hash);
81 * Take the key fields of a reply packet, and convert it to a
84 * i.e. take a reply packet, and find the hash of the request packet
85 * that asked for the reply. To do this, we hash the reverse fields
86 * of the request. e.g. where the request does (src, dst), we do
89 uint32_t fr_reply_packet_hash(const RADIUS_PACKET *packet)
93 hash = fr_hash(&packet->sockfd, sizeof(packet->sockfd));
94 hash = fr_hash_update(&packet->id, sizeof(packet->id), hash);
95 hash = fr_hash_update(&packet->src_port, sizeof(packet->src_port),
97 hash = fr_hash_update(&packet->dst_port,
98 sizeof(packet->dst_port), hash);
99 hash = fr_hash_update(&packet->src_ipaddr.af,
100 sizeof(packet->src_ipaddr.af), hash);
103 * The caller ensures that src & dst AF are the same.
105 switch (packet->src_ipaddr.af) {
107 hash = fr_hash_update(&packet->dst_ipaddr.ipaddr.ip4addr,
108 sizeof(packet->dst_ipaddr.ipaddr.ip4addr),
110 hash = fr_hash_update(&packet->src_ipaddr.ipaddr.ip4addr,
111 sizeof(packet->src_ipaddr.ipaddr.ip4addr),
115 hash = fr_hash_update(&packet->dst_ipaddr.ipaddr.ip6addr,
116 sizeof(packet->dst_ipaddr.ipaddr.ip6addr),
118 hash = fr_hash_update(&packet->src_ipaddr.ipaddr.ip6addr,
119 sizeof(packet->src_ipaddr.ipaddr.ip6addr),
126 return fr_hash_update(&packet->id, sizeof(packet->id), hash);
131 * See if two packets are identical.
133 * Note that we do NOT compare the authentication vectors.
134 * That's because if the authentication vector is different,
135 * it means that the NAS has given up on the earlier request.
137 int fr_packet_cmp(const RADIUS_PACKET *a, const RADIUS_PACKET *b)
141 if (a->sockfd < b->sockfd) return -1;
142 if (a->sockfd > b->sockfd) return +1;
144 if (a->id < b->id) return -1;
145 if (a->id > b->id) return +1;
147 if (a->src_port < b->src_port) return -1;
148 if (a->src_port > b->src_port) return +1;
150 if (a->dst_port < b->dst_port) return -1;
151 if (a->dst_port > b->dst_port) return +1;
153 rcode = fr_ipaddr_cmp(&a->dst_ipaddr, &b->dst_ipaddr);
154 if (rcode != 0) return rcode;
155 return fr_ipaddr_cmp(&a->src_ipaddr, &b->src_ipaddr);
158 int fr_inaddr_any(fr_ipaddr_t *ipaddr)
161 if (ipaddr->af == AF_INET) {
162 if (ipaddr->ipaddr.ip4addr.s_addr == INADDR_ANY) {
166 #ifdef HAVE_STRUCT_SOCKADDR_IN6
167 } else if (ipaddr->af == AF_INET6) {
168 if (IN6_IS_ADDR_UNSPECIFIED(&(ipaddr->ipaddr.ip6addr))) {
174 fr_strerror_printf("Unknown address family");
183 * Create a fake "request" from a reply, for later lookup.
185 void fr_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;
197 int fr_nonblock(UNUSED int fd)
203 flags = fcntl(fd, F_GETFL, NULL);
206 return fcntl(fd, F_SETFL, flags);
213 * Open a socket on the given IP and port.
215 int fr_socket(fr_ipaddr_t *ipaddr, int port)
218 struct sockaddr_storage salocal;
221 if ((port < 0) || (port > 65535)) {
222 fr_strerror_printf("Port %d is out of allowed bounds", port);
226 sockfd = socket(ipaddr->af, SOCK_DGRAM, 0);
228 fr_strerror_printf("cannot open socket: %s", strerror(errno));
232 #ifdef WITH_UDPFROMTO
234 * Initialize udpfromto for all sockets.
236 if (udpfromto_init(sockfd) != 0) {
238 fr_strerror_printf("cannot initialize udpfromto: %s", strerror(errno));
243 if (fr_nonblock(sockfd) < 0) {
248 if (!fr_ipaddr2sockaddr(ipaddr, port, &salocal, &salen)) {
252 #ifdef HAVE_STRUCT_SOCKADDR_IN6
253 if (ipaddr->af == AF_INET6) {
255 * Listening on '::' does NOT get you IPv4 to
256 * IPv6 mapping. You've got to listen on an IPv4
257 * address, too. This makes the rest of the server
258 * design a little simpler.
262 if (IN6_IS_ADDR_UNSPECIFIED(&ipaddr->ipaddr.ip6addr)) {
265 setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY,
266 (char *)&on, sizeof(on));
268 #endif /* IPV6_V6ONLY */
270 #endif /* HAVE_STRUCT_SOCKADDR_IN6 */
272 if (ipaddr->af == AF_INET) {
275 #if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
277 * Disable PMTU discovery. On Linux, this
278 * also makes sure that the "don't fragment"
281 flag = IP_PMTUDISC_DONT;
282 setsockopt(sockfd, IPPROTO_IP, IP_MTU_DISCOVER,
283 &flag, sizeof(flag));
286 #if defined(IP_DONTFRAG)
288 * Ensure that the "don't fragment" flag is zero.
291 setsockopt(sockfd, IPPROTO_IP, IP_DONTFRAG,
292 &flag, sizeof(flag));
296 if (bind(sockfd, (struct sockaddr *) &salocal, salen) < 0) {
298 fr_strerror_printf("cannot bind socket: %s", strerror(errno));
307 * We need to keep track of the socket & it's IP/port.
309 typedef struct fr_packet_socket_t {
316 fr_ipaddr_t src_ipaddr;
320 fr_ipaddr_t dst_ipaddr;
330 } fr_packet_socket_t;
333 #define FNV_MAGIC_PRIME (0x01000193)
334 #define MAX_SOCKETS (256)
335 #define SOCKOFFSET_MASK (MAX_SOCKETS - 1)
336 #define SOCK2OFFSET(sockfd) ((sockfd * FNV_MAGIC_PRIME) & SOCKOFFSET_MASK)
338 #define MAX_QUEUES (8)
341 * Structure defining a list of packets (incoming or outgoing)
342 * that should be managed.
344 struct fr_packet_list_t {
351 fr_packet_socket_t sockets[MAX_SOCKETS];
356 * Ugh. Doing this on every sent/received packet is not nice.
358 static fr_packet_socket_t *fr_socket_find(fr_packet_list_t *pl,
363 i = start = SOCK2OFFSET(sockfd);
365 do { /* make this hack slightly more efficient */
366 if (pl->sockets[i].sockfd == sockfd) return &pl->sockets[i];
368 i = (i + 1) & SOCKOFFSET_MASK;
369 } while (i != start);
374 int fr_packet_list_socket_freeze(fr_packet_list_t *pl, int sockfd)
376 fr_packet_socket_t *ps;
380 ps = fr_socket_find(pl, sockfd);
387 int fr_packet_list_socket_remove(fr_packet_list_t *pl, int sockfd,
390 fr_packet_socket_t *ps;
394 ps = fr_socket_find(pl, sockfd);
398 * FIXME: Allow the caller forcibly discard these?
400 if (ps->num_outgoing != 0) return 0;
403 if (pctx) *pctx = ps->ctx;
408 int fr_packet_list_socket_add(fr_packet_list_t *pl, int sockfd,
409 fr_ipaddr_t *dst_ipaddr, int dst_port,
413 struct sockaddr_storage src;
414 socklen_t sizeof_src;
415 fr_packet_socket_t *ps;
417 if (!pl || !dst_ipaddr || (dst_ipaddr->af == AF_UNSPEC)) {
418 fr_strerror_printf("Invalid argument");
423 i = start = SOCK2OFFSET(sockfd);
426 if (pl->sockets[i].sockfd == -1) {
427 ps = &pl->sockets[i];
432 i = (i + 1) & SOCKOFFSET_MASK;
433 } while (i != start);
436 fr_strerror_printf("All socket entries are full");
440 memset(ps, 0, sizeof(*ps));
444 sizeof_src = sizeof(ps->type);
446 if (getsockopt(sockfd, SOL_SOCKET, SO_TYPE, &ps->type, &sizeof_src) < 0) {
447 fr_strerror_printf("%s", strerror(errno));
454 * Get address family, etc. first, so we know if we
455 * need to do udpfromto.
457 * FIXME: udpfromto also does this, but it's not
458 * a critical problem.
460 sizeof_src = sizeof(src);
461 memset(&src, 0, sizeof_src);
462 if (getsockname(sockfd, (struct sockaddr *) &src,
464 fr_strerror_printf("%s", strerror(errno));
468 if (!fr_sockaddr2ipaddr(&src, sizeof_src, &ps->src_ipaddr,
470 fr_strerror_printf("Failed to get IP");
474 ps->dst_ipaddr = *dst_ipaddr;
475 ps->dst_port = dst_port;
477 ps->src_any = fr_inaddr_any(&ps->src_ipaddr);
478 if (ps->src_any < 0) return 0;
480 ps->dst_any = fr_inaddr_any(&ps->dst_ipaddr);
481 if (ps->dst_any < 0) return 0;
484 * As the last step before returning.
491 static uint32_t packet_entry_hash(const void *data)
493 return fr_request_packet_hash(*(const RADIUS_PACKET * const *) data);
496 static int packet_entry_cmp(const void *one, const void *two)
498 const RADIUS_PACKET * const *a = one;
499 const RADIUS_PACKET * const *b = two;
501 return fr_packet_cmp(*a, *b);
504 void fr_packet_list_free(fr_packet_list_t *pl)
508 fr_hash_table_free(pl->ht);
514 * Caller is responsible for managing the packet entries.
516 fr_packet_list_t *fr_packet_list_create(int alloc_id)
519 fr_packet_list_t *pl;
521 pl = malloc(sizeof(*pl));
522 if (!pl) return NULL;
523 memset(pl, 0, sizeof(*pl));
525 pl->ht = fr_hash_table_create(packet_entry_hash,
529 fr_packet_list_free(pl);
533 for (i = 0; i < MAX_SOCKETS; i++) {
534 pl->sockets[i].sockfd = -1;
537 pl->alloc_id = alloc_id;
544 * If pl->alloc_id is set, then fr_packet_list_id_alloc() MUST
545 * be called before inserting the packet into the list!
547 int fr_packet_list_insert(fr_packet_list_t *pl,
548 RADIUS_PACKET **request_p)
550 if (!pl || !request_p || !*request_p) return 0;
552 (*request_p)->hash = fr_request_packet_hash(*request_p);
554 return fr_hash_table_insert(pl->ht, request_p);
557 RADIUS_PACKET **fr_packet_list_find(fr_packet_list_t *pl,
558 RADIUS_PACKET *request)
560 if (!pl || !request) return 0;
562 return fr_hash_table_finddata(pl->ht, &request);
567 * This presumes that the reply has dst_ipaddr && dst_port set up
568 * correctly (i.e. real IP, or "*").
570 RADIUS_PACKET **fr_packet_list_find_byreply(fr_packet_list_t *pl,
571 RADIUS_PACKET *reply)
573 RADIUS_PACKET my_request, *request;
574 fr_packet_socket_t *ps;
576 if (!pl || !reply) return NULL;
578 ps = fr_socket_find(pl, reply->sockfd);
579 if (!ps) return NULL;
582 * Initialize request from reply, AND from the source
583 * IP & port of this socket. The client may have bound
584 * the socket to 0, in which case it's some random port,
585 * that is NOT in the original request->src_port.
587 my_request.sockfd = reply->sockfd;
588 my_request.id = reply->id;
591 my_request.src_ipaddr = ps->src_ipaddr;
593 my_request.src_ipaddr = reply->dst_ipaddr;
595 my_request.src_port = ps->src_port;
597 my_request.dst_ipaddr = reply->src_ipaddr;
598 my_request.dst_port = reply->src_port;
601 request = &my_request;
603 return fr_hash_table_finddata(pl->ht, &request);
607 RADIUS_PACKET **fr_packet_list_yank(fr_packet_list_t *pl,
608 RADIUS_PACKET *request)
610 if (!pl || !request) return NULL;
612 return fr_hash_table_yank(pl->ht, &request);
615 int fr_packet_list_num_elements(fr_packet_list_t *pl)
619 return fr_hash_table_num_elements(pl->ht);
624 * 1 == ID was allocated & assigned
625 * 0 == error allocating memory
626 * -1 == all ID's are used, caller should open a new socket.
628 * Note that this ALSO assigns a socket to use, and updates
629 * packet->request->src_ipaddr && packet->request->src_port
631 * In multi-threaded systems, the calls to id_alloc && id_free
632 * should be protected by a mutex. This does NOT have to be
633 * the same mutex as the one protecting the insert/find/yank
636 * We assume that the packet has dst_ipaddr && dst_port
637 * already initialized. We will use those to find an
638 * outgoing socket. The request MAY also have src_ipaddr set.
640 * We also assume that the sender doesn't care which protocol
643 int fr_packet_list_id_alloc(fr_packet_list_t *pl,
644 RADIUS_PACKET *request, void **pctx)
646 int i, j, k, fd, id, start_i, start_j, start_k;
648 fr_packet_socket_t *ps;
650 if ((request->dst_ipaddr.af == AF_UNSPEC) ||
651 (request->dst_port == 0)) {
652 fr_strerror_printf("No destination address/port specified");
657 * Special case: unspec == "don't care"
659 if (request->src_ipaddr.af == AF_UNSPEC) {
660 memset(&request->src_ipaddr, 0, sizeof(request->src_ipaddr));
661 request->src_ipaddr.af = request->dst_ipaddr.af;
664 src_any = fr_inaddr_any(&request->src_ipaddr);
665 if (src_any < 0) return 0;
668 * MUST specify a destination address.
670 if (fr_inaddr_any(&request->dst_ipaddr) != 0) return 0;
673 * FIXME: Go to an LRU system. This prevents ID re-use
674 * for as long as possible. The main problem with that
675 * approach is that it requires us to populate the
676 * LRU/FIFO when we add a new socket, or a new destination,
677 * which can be expensive.
679 * The LRU can be avoided if the caller takes care to free
680 * Id's only when all responses have been received, OR after
683 * Right now, the random approach is almost OK... it's
684 * brute-force over all of the available ID's, BUT using
685 * random numbers for everything spreads the load a bit.
687 * The old method had a hash lookup on allocation AND
688 * on free. The new method has brute-force on allocation,
689 * and near-zero cost on free.
693 start_i = fr_rand() & SOCKOFFSET_MASK;
695 #define ID_i ((i + start_i) & SOCKOFFSET_MASK)
696 for (i = 0; i < MAX_SOCKETS; i++) {
697 if (pl->sockets[ID_i].sockfd == -1) continue; /* paranoia */
699 ps = &(pl->sockets[ID_i]);
702 * This socket is marked as "don't use for new
703 * packets". But we can still receive packets
704 * that are outstanding.
706 if (ps->dont_use) continue;
709 * All IDs are allocated: ignore it.
711 if (ps->num_outgoing == 256) continue;
714 * MUST match dst port, if one has been given.
716 if ((ps->dst_port != 0) &&
717 (ps->dst_port != request->dst_port)) continue;
720 * We're sourcing from *, and they asked for a
721 * specific source address: ignore it.
723 if (ps->src_any && !src_any) continue;
726 * We're sourcing from a specific IP, and they
727 * asked for a source IP that isn't us: ignore
730 if (!ps->src_any && !src_any &&
731 (fr_ipaddr_cmp(&request->src_ipaddr,
732 &ps->src_ipaddr) != 0)) continue;
735 * UDP sockets are allowed to match
736 * destination IPs exactly, OR a socket
737 * with destination * is allowed to match
738 * any requested destination.
740 * TCP sockets must match the destination
741 * exactly. They *always* have dst_any=0,
742 * so the first check always matches.
745 (fr_ipaddr_cmp(&request->dst_ipaddr,
746 &ps->dst_ipaddr) != 0)) continue;
749 * Otherwise, this socket is OK to use.
753 * Look for a free Id, starting from a random number.
755 start_j = fr_rand() & 0x1f;
756 #define ID_j ((j + start_j) & 0x1f)
757 for (j = 0; j < 32; j++) {
758 if (ps->id[ID_j] == 0xff) continue;
761 start_k = fr_rand() & 0x07;
762 #define ID_k ((k + start_k) & 0x07)
763 for (k = 0; k < 8; k++) {
764 if ((ps->id[ID_j] & (1 << ID_k)) != 0) continue;
766 ps->id[ID_j] |= (1 << ID_k);
767 id = (ID_j * 8) + ID_k;
781 * Ask the caller to allocate a new ID.
783 if (fd < 0) return 0;
789 * Set the ID, source IP, and source port.
793 request->sockfd = ps->sockfd;
794 request->src_ipaddr = ps->src_ipaddr;
795 request->src_port = ps->src_port;
797 if (pctx) *pctx = ps->ctx;
803 * Should be called AFTER yanking it from the list, so that
804 * any newly inserted entries don't collide with this one.
806 int fr_packet_list_id_free(fr_packet_list_t *pl,
807 RADIUS_PACKET *request)
809 fr_packet_socket_t *ps;
811 if (!pl || !request) return 0;
813 ps = fr_socket_find(pl, request->sockfd);
817 if (!ps->id[(request->id >> 3) & 0x1f] & (1 << (request->id & 0x07))) {
822 ps->id[(request->id >> 3) & 0x1f] &= ~(1 << (request->id & 0x07));
823 request->hash = 0; /* invalidate the cached hash */
831 int fr_packet_list_walk(fr_packet_list_t *pl, void *ctx,
832 fr_hash_table_walk_t callback)
834 if (!pl || !callback) return 0;
836 return fr_hash_table_walk(pl->ht, callback, ctx);
839 int fr_packet_list_fd_set(fr_packet_list_t *pl, fd_set *set)
843 if (!pl || !set) return 0;
847 for (i = 0; i < MAX_SOCKETS; i++) {
848 if (pl->sockets[i].sockfd == -1) continue;
849 FD_SET(pl->sockets[i].sockfd, set);
850 if (pl->sockets[i].sockfd > maxfd) {
851 maxfd = pl->sockets[i].sockfd;
855 if (maxfd < 0) return -1;
861 * Round-robins the receivers, without priority.
863 * FIXME: Add sockfd, if -1, do round-robin, else do sockfd
866 RADIUS_PACKET *fr_packet_list_recv(fr_packet_list_t *pl, fd_set *set)
869 RADIUS_PACKET *packet;
871 if (!pl || !set) return NULL;
873 start = pl->last_recv;
876 start &= SOCKOFFSET_MASK;
878 if (pl->sockets[start].sockfd == -1) continue;
880 if (!FD_ISSET(pl->sockets[start].sockfd, set)) continue;
883 if (pl->sockets[start].type == SOCK_STREAM) {
884 packet = fr_tcp_recv(pl->sockets[start].sockfd, 0);
887 packet = rad_recv(pl->sockets[start].sockfd, 0);
888 if (!packet) continue;
891 * Call fr_packet_list_find_byreply(). If it
892 * doesn't find anything, discard the reply.
895 pl->last_recv = start;
897 } while (start != pl->last_recv);
902 int fr_packet_list_num_incoming(fr_packet_list_t *pl)
908 num_elements = fr_hash_table_num_elements(pl->ht);
909 if (num_elements < pl->num_outgoing) return 0; /* panic! */
911 return num_elements - pl->num_outgoing;
914 int fr_packet_list_num_outgoing(fr_packet_list_t *pl)
918 return pl->num_outgoing;