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 * See if two packets are identical.
37 * Note that we do NOT compare the authentication vectors.
38 * That's because if the authentication vector is different,
39 * it means that the NAS has given up on the earlier request.
41 int fr_packet_cmp(const RADIUS_PACKET *a, const RADIUS_PACKET *b)
45 rcode = a->id - b->id;
46 if (rcode != 0) return rcode;
48 rcode = (int) a->src_port - (int) b->src_port;
49 if (rcode != 0) return rcode;
51 rcode = (int) a->dst_port - (int) b->dst_port;
52 if (rcode != 0) return rcode;
54 rcode = a->sockfd - b->sockfd;
55 if (rcode != 0) return rcode;
57 rcode = fr_ipaddr_cmp(&a->src_ipaddr, &b->src_ipaddr);
58 if (rcode != 0) return rcode;
60 return fr_ipaddr_cmp(&a->dst_ipaddr, &b->dst_ipaddr);
63 int fr_inaddr_any(fr_ipaddr_t *ipaddr)
66 if (ipaddr->af == AF_INET) {
67 if (ipaddr->ipaddr.ip4addr.s_addr == INADDR_ANY) {
71 #ifdef HAVE_STRUCT_SOCKADDR_IN6
72 } else if (ipaddr->af == AF_INET6) {
73 if (IN6_IS_ADDR_UNSPECIFIED(&(ipaddr->ipaddr.ip6addr))) {
79 fr_strerror_printf("Unknown address family");
88 * Create a fake "request" from a reply, for later lookup.
90 void fr_request_from_reply(RADIUS_PACKET *request,
91 const RADIUS_PACKET *reply)
93 request->sockfd = reply->sockfd;
94 request->id = reply->id;
95 request->src_port = reply->dst_port;
96 request->dst_port = reply->src_port;
97 request->src_ipaddr = reply->dst_ipaddr;
98 request->dst_ipaddr = reply->src_ipaddr;
102 int fr_nonblock(UNUSED int fd)
108 flags = fcntl(fd, F_GETFL, NULL);
111 return fcntl(fd, F_SETFL, flags);
118 * Open a socket on the given IP and port.
120 int fr_socket(fr_ipaddr_t *ipaddr, int port)
123 struct sockaddr_storage salocal;
126 if ((port < 0) || (port > 65535)) {
127 fr_strerror_printf("Port %d is out of allowed bounds", port);
131 sockfd = socket(ipaddr->af, SOCK_DGRAM, 0);
133 fr_strerror_printf("cannot open socket: %s", strerror(errno));
137 #ifdef WITH_UDPFROMTO
139 * Initialize udpfromto for all sockets.
141 if (udpfromto_init(sockfd) != 0) {
143 fr_strerror_printf("cannot initialize udpfromto: %s", strerror(errno));
148 if (fr_nonblock(sockfd) < 0) {
153 if (!fr_ipaddr2sockaddr(ipaddr, port, &salocal, &salen)) {
157 #ifdef HAVE_STRUCT_SOCKADDR_IN6
158 if (ipaddr->af == AF_INET6) {
160 * Listening on '::' does NOT get you IPv4 to
161 * IPv6 mapping. You've got to listen on an IPv4
162 * address, too. This makes the rest of the server
163 * design a little simpler.
167 if (IN6_IS_ADDR_UNSPECIFIED(&ipaddr->ipaddr.ip6addr)) {
170 setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY,
171 (char *)&on, sizeof(on));
173 #endif /* IPV6_V6ONLY */
175 #endif /* HAVE_STRUCT_SOCKADDR_IN6 */
177 if (ipaddr->af == AF_INET) {
180 #if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
182 * Disable PMTU discovery. On Linux, this
183 * also makes sure that the "don't fragment"
186 flag = IP_PMTUDISC_DONT;
187 setsockopt(sockfd, IPPROTO_IP, IP_MTU_DISCOVER,
188 &flag, sizeof(flag));
191 #if defined(IP_DONTFRAG)
193 * Ensure that the "don't fragment" flag is zero.
196 setsockopt(sockfd, IPPROTO_IP, IP_DONTFRAG,
197 &flag, sizeof(flag));
201 if (bind(sockfd, (struct sockaddr *) &salocal, salen) < 0) {
203 fr_strerror_printf("cannot bind socket: %s", strerror(errno));
212 * We need to keep track of the socket & it's IP/port.
214 typedef struct fr_packet_socket_t {
221 fr_ipaddr_t src_ipaddr;
225 fr_ipaddr_t dst_ipaddr;
235 } fr_packet_socket_t;
238 #define FNV_MAGIC_PRIME (0x01000193)
239 #define MAX_SOCKETS (256)
240 #define SOCKOFFSET_MASK (MAX_SOCKETS - 1)
241 #define SOCK2OFFSET(sockfd) ((sockfd * FNV_MAGIC_PRIME) & SOCKOFFSET_MASK)
243 #define MAX_QUEUES (8)
246 * Structure defining a list of packets (incoming or outgoing)
247 * that should be managed.
249 struct fr_packet_list_t {
257 fr_packet_socket_t sockets[MAX_SOCKETS];
262 * Ugh. Doing this on every sent/received packet is not nice.
264 static fr_packet_socket_t *fr_socket_find(fr_packet_list_t *pl,
269 i = start = SOCK2OFFSET(sockfd);
271 do { /* make this hack slightly more efficient */
272 if (pl->sockets[i].sockfd == sockfd) return &pl->sockets[i];
274 i = (i + 1) & SOCKOFFSET_MASK;
275 } while (i != start);
280 int fr_packet_list_socket_freeze(fr_packet_list_t *pl, int sockfd)
282 fr_packet_socket_t *ps;
285 fr_strerror_printf("Invalid argument");
289 ps = fr_socket_find(pl, sockfd);
291 fr_strerror_printf("No such socket");
299 int fr_packet_list_socket_thaw(fr_packet_list_t *pl, int sockfd)
301 fr_packet_socket_t *ps;
305 ps = fr_socket_find(pl, sockfd);
312 int fr_packet_list_socket_remove(fr_packet_list_t *pl, int sockfd,
315 fr_packet_socket_t *ps;
319 ps = fr_socket_find(pl, sockfd);
323 * FIXME: Allow the caller forcibly discard these?
325 if (ps->num_outgoing != 0) return 0;
329 if (pctx) *pctx = ps->ctx;
334 int fr_packet_list_socket_add(fr_packet_list_t *pl, int sockfd, int proto,
335 fr_ipaddr_t *dst_ipaddr, int dst_port,
339 struct sockaddr_storage src;
340 socklen_t sizeof_src;
341 fr_packet_socket_t *ps;
343 if (!pl || !dst_ipaddr || (dst_ipaddr->af == AF_UNSPEC)) {
344 fr_strerror_printf("Invalid argument");
348 if (pl->num_sockets >= MAX_SOCKETS) {
349 fr_strerror_printf("Too many open sockets");
354 if (proto != IPPROTO_UDP) {
355 fr_strerror_printf("only UDP is supported");
361 i = start = SOCK2OFFSET(sockfd);
364 if (pl->sockets[i].sockfd == -1) {
365 ps = &pl->sockets[i];
369 i = (i + 1) & SOCKOFFSET_MASK;
370 } while (i != start);
373 fr_strerror_printf("All socket entries are full");
377 memset(ps, 0, sizeof(*ps));
384 * Get address family, etc. first, so we know if we
385 * need to do udpfromto.
387 * FIXME: udpfromto also does this, but it's not
388 * a critical problem.
390 sizeof_src = sizeof(src);
391 memset(&src, 0, sizeof_src);
392 if (getsockname(sockfd, (struct sockaddr *) &src,
394 fr_strerror_printf("%s", strerror(errno));
398 if (!fr_sockaddr2ipaddr(&src, sizeof_src, &ps->src_ipaddr,
400 fr_strerror_printf("Failed to get IP");
404 ps->dst_ipaddr = *dst_ipaddr;
405 ps->dst_port = dst_port;
407 ps->src_any = fr_inaddr_any(&ps->src_ipaddr);
408 if (ps->src_any < 0) return 0;
410 ps->dst_any = fr_inaddr_any(&ps->dst_ipaddr);
411 if (ps->dst_any < 0) return 0;
414 * As the last step before returning.
422 static int packet_entry_cmp(const void *one, const void *two)
424 const RADIUS_PACKET * const *a = one;
425 const RADIUS_PACKET * const *b = two;
427 return fr_packet_cmp(*a, *b);
430 void fr_packet_list_free(fr_packet_list_t *pl)
434 rbtree_free(pl->tree);
440 * Caller is responsible for managing the packet entries.
442 fr_packet_list_t *fr_packet_list_create(int alloc_id)
445 fr_packet_list_t *pl;
447 pl = malloc(sizeof(*pl));
448 if (!pl) return NULL;
449 memset(pl, 0, sizeof(*pl));
451 pl->tree = rbtree_create(packet_entry_cmp, NULL, 0);
453 fr_packet_list_free(pl);
457 for (i = 0; i < MAX_SOCKETS; i++) {
458 pl->sockets[i].sockfd = -1;
461 pl->alloc_id = alloc_id;
468 * If pl->alloc_id is set, then fr_packet_list_id_alloc() MUST
469 * be called before inserting the packet into the list!
471 int fr_packet_list_insert(fr_packet_list_t *pl,
472 RADIUS_PACKET **request_p)
474 if (!pl || !request_p || !*request_p) return 0;
476 return rbtree_insert(pl->tree, request_p);
479 RADIUS_PACKET **fr_packet_list_find(fr_packet_list_t *pl,
480 RADIUS_PACKET *request)
482 if (!pl || !request) return 0;
484 return rbtree_finddata(pl->tree, &request);
489 * This presumes that the reply has dst_ipaddr && dst_port set up
490 * correctly (i.e. real IP, or "*").
492 RADIUS_PACKET **fr_packet_list_find_byreply(fr_packet_list_t *pl,
493 RADIUS_PACKET *reply)
495 RADIUS_PACKET my_request, *request;
496 fr_packet_socket_t *ps;
498 if (!pl || !reply) return NULL;
500 ps = fr_socket_find(pl, reply->sockfd);
501 if (!ps) return NULL;
504 * Initialize request from reply, AND from the source
505 * IP & port of this socket. The client may have bound
506 * the socket to 0, in which case it's some random port,
507 * that is NOT in the original request->src_port.
509 my_request.sockfd = reply->sockfd;
510 my_request.id = reply->id;
513 my_request.src_ipaddr = ps->src_ipaddr;
515 my_request.src_ipaddr = reply->dst_ipaddr;
517 my_request.src_port = ps->src_port;
519 my_request.dst_ipaddr = reply->src_ipaddr;
520 my_request.dst_port = reply->src_port;
522 request = &my_request;
524 return rbtree_finddata(pl->tree, &request);
528 void fr_packet_list_yank(fr_packet_list_t *pl, RADIUS_PACKET *request)
532 if (!pl || !request) return;
534 node = rbtree_find(pl->tree, &request);
537 rbtree_delete(pl->tree, node);
540 int fr_packet_list_num_elements(fr_packet_list_t *pl)
544 return rbtree_num_elements(pl->tree);
549 * 1 == ID was allocated & assigned
550 * 0 == error allocating memory
551 * -1 == all ID's are used, caller should open a new socket.
553 * Note that this ALSO assigns a socket to use, and updates
554 * packet->request->src_ipaddr && packet->request->src_port
556 * In multi-threaded systems, the calls to id_alloc && id_free
557 * should be protected by a mutex. This does NOT have to be
558 * the same mutex as the one protecting the insert/find/yank
561 * We assume that the packet has dst_ipaddr && dst_port
562 * already initialized. We will use those to find an
563 * outgoing socket. The request MAY also have src_ipaddr set.
565 * We also assume that the sender doesn't care which protocol
568 int fr_packet_list_id_alloc(fr_packet_list_t *pl, int proto,
569 RADIUS_PACKET *request, void **pctx)
571 int i, j, k, fd, id, start_i, start_j, start_k;
573 fr_packet_socket_t *ps;
575 if ((request->dst_ipaddr.af == AF_UNSPEC) ||
576 (request->dst_port == 0)) {
577 fr_strerror_printf("No destination address/port specified");
582 if ((proto != 0) && (proto != IPPROTO_UDP)) {
583 fr_strerror_printf("Invalid destination protocol");
589 * Special case: unspec == "don't care"
591 if (request->src_ipaddr.af == AF_UNSPEC) {
592 memset(&request->src_ipaddr, 0, sizeof(request->src_ipaddr));
593 request->src_ipaddr.af = request->dst_ipaddr.af;
596 src_any = fr_inaddr_any(&request->src_ipaddr);
597 if (src_any < 0) return 0;
600 * MUST specify a destination address.
602 if (fr_inaddr_any(&request->dst_ipaddr) != 0) return 0;
605 * FIXME: Go to an LRU system. This prevents ID re-use
606 * for as long as possible. The main problem with that
607 * approach is that it requires us to populate the
608 * LRU/FIFO when we add a new socket, or a new destination,
609 * which can be expensive.
611 * The LRU can be avoided if the caller takes care to free
612 * Id's only when all responses have been received, OR after
615 * Right now, the random approach is almost OK... it's
616 * brute-force over all of the available ID's, BUT using
617 * random numbers for everything spreads the load a bit.
619 * The old method had a hash lookup on allocation AND
620 * on free. The new method has brute-force on allocation,
621 * and near-zero cost on free.
625 start_i = fr_rand() & SOCKOFFSET_MASK;
627 #define ID_i ((i + start_i) & SOCKOFFSET_MASK)
628 for (i = 0; i < MAX_SOCKETS; i++) {
629 if (pl->sockets[ID_i].sockfd == -1) continue; /* paranoia */
631 ps = &(pl->sockets[ID_i]);
634 * This socket is marked as "don't use for new
635 * packets". But we can still receive packets
636 * that are outstanding.
638 if (ps->dont_use) continue;
641 * All IDs are allocated: ignore it.
643 if (ps->num_outgoing == 256) continue;
646 if (ps->proto != proto) continue;
650 * Address families don't match, skip it.
652 if (ps->src_ipaddr.af != request->dst_ipaddr.af) continue;
655 * MUST match dst port, if we have one.
657 if ((ps->dst_port != 0) &&
658 (ps->dst_port != request->dst_port)) continue;
661 * MUST match requested src port, if one has been given.
663 if ((request->src_port != 0) &&
664 (ps->src_port != request->src_port)) continue;
667 * We're sourcing from *, and they asked for a
668 * specific source address: ignore it.
670 if (ps->src_any && !src_any) continue;
673 * We're sourcing from a specific IP, and they
674 * asked for a source IP that isn't us: ignore
677 if (!ps->src_any && !src_any &&
678 (fr_ipaddr_cmp(&request->src_ipaddr,
679 &ps->src_ipaddr) != 0)) continue;
682 * UDP sockets are allowed to match
683 * destination IPs exactly, OR a socket
684 * with destination * is allowed to match
685 * any requested destination.
687 * TCP sockets must match the destination
688 * exactly. They *always* have dst_any=0,
689 * so the first check always matches.
692 (fr_ipaddr_cmp(&request->dst_ipaddr,
693 &ps->dst_ipaddr) != 0)) continue;
696 * Otherwise, this socket is OK to use.
700 * Look for a free Id, starting from a random number.
702 start_j = fr_rand() & 0x1f;
703 #define ID_j ((j + start_j) & 0x1f)
704 for (j = 0; j < 32; j++) {
705 if (ps->id[ID_j] == 0xff) continue;
708 start_k = fr_rand() & 0x07;
709 #define ID_k ((k + start_k) & 0x07)
710 for (k = 0; k < 8; k++) {
711 if ((ps->id[ID_j] & (1 << ID_k)) != 0) continue;
713 ps->id[ID_j] |= (1 << ID_k);
714 id = (ID_j * 8) + ID_k;
728 * Ask the caller to allocate a new ID.
730 if (fd < 0) return 0;
736 * Set the ID, source IP, and source port.
740 request->sockfd = ps->sockfd;
741 request->src_ipaddr = ps->src_ipaddr;
742 request->src_port = ps->src_port;
744 if (pctx) *pctx = ps->ctx;
750 * Should be called AFTER yanking it from the list, so that
751 * any newly inserted entries don't collide with this one.
753 int fr_packet_list_id_free(fr_packet_list_t *pl,
754 RADIUS_PACKET *request)
756 fr_packet_socket_t *ps;
758 if (!pl || !request) return 0;
760 ps = fr_socket_find(pl, request->sockfd);
764 if (!ps->id[(request->id >> 3) & 0x1f] & (1 << (request->id & 0x07))) {
769 ps->id[(request->id >> 3) & 0x1f] &= ~(1 << (request->id & 0x07));
777 int fr_packet_list_walk(fr_packet_list_t *pl, void *ctx,
778 fr_hash_table_walk_t callback)
780 if (!pl || !callback) return 0;
782 return rbtree_walk(pl->tree, InOrder, callback, ctx);
785 int fr_packet_list_fd_set(fr_packet_list_t *pl, fd_set *set)
789 if (!pl || !set) return 0;
793 for (i = 0; i < MAX_SOCKETS; i++) {
794 if (pl->sockets[i].sockfd == -1) continue;
795 FD_SET(pl->sockets[i].sockfd, set);
796 if (pl->sockets[i].sockfd > maxfd) {
797 maxfd = pl->sockets[i].sockfd;
801 if (maxfd < 0) return -1;
807 * Round-robins the receivers, without priority.
809 * FIXME: Add sockfd, if -1, do round-robin, else do sockfd
812 RADIUS_PACKET *fr_packet_list_recv(fr_packet_list_t *pl, fd_set *set)
815 RADIUS_PACKET *packet;
817 if (!pl || !set) return NULL;
819 start = pl->last_recv;
822 start &= SOCKOFFSET_MASK;
824 if (pl->sockets[start].sockfd == -1) continue;
826 if (!FD_ISSET(pl->sockets[start].sockfd, set)) continue;
829 if (pl->sockets[start].proto == IPPROTO_TCP) {
830 packet = fr_tcp_recv(pl->sockets[start].sockfd, 0);
833 packet = rad_recv(pl->sockets[start].sockfd, 0);
834 if (!packet) continue;
837 * Call fr_packet_list_find_byreply(). If it
838 * doesn't find anything, discard the reply.
841 pl->last_recv = start;
843 } while (start != pl->last_recv);
848 int fr_packet_list_num_incoming(fr_packet_list_t *pl)
854 num_elements = rbtree_num_elements(pl->tree);
855 if (num_elements < pl->num_outgoing) return 0; /* panic! */
857 return num_elements - pl->num_outgoing;
860 int fr_packet_list_num_outgoing(fr_packet_list_t *pl)
864 return pl->num_outgoing;