예제 #1
0
int main(int argc, char **argv)
{

	static uint16_t		server_port = 0;
	static int		packet_code = 0;
	static fr_ipaddr_t	server_ipaddr;
	static fr_ipaddr_t	client_ipaddr;

	int			c;
	char const		*raddb_dir = RADDBDIR;
	char const		*dict_dir = DICTDIR;
	char const		*filename = NULL;

	RADIUS_PACKET		*request = NULL;
	RADIUS_PACKET		*reply = NULL;

	TALLOC_CTX		*autofree = talloc_autofree_context();

	int			ret;

	fr_debug_lvl = 1;
	fr_log_fp = stdout;

	while ((c = getopt(argc, argv, "d:D:f:hr:t:vxi:")) != -1) switch(c) {
		case 'D':
			dict_dir = optarg;
			break;

		case 'd':
			raddb_dir = optarg;
			break;

		case 'f':
			filename = optarg;
			break;

		case 'i':
			iface = optarg;
			break;

		case 'r':
			if (!isdigit((int) *optarg)) usage();
			retries = atoi(optarg);
			if ((retries == 0) || (retries > 1000)) usage();
			break;

		case 't':
			if (!isdigit((int) *optarg)) usage();
			timeout = atof(optarg);
			break;

		case 'v':
			DEBUG("%s", dhcpclient_version);
			exit(0);

		case 'x':
			fr_debug_lvl++;
			break;

		case 'h':
		default:
			usage();
	}
	argc -= (optind - 1);
	argv += (optind - 1);

	if (argc < 2) usage();

	/*	convert timeout to a struct timeval */
#define USEC 1000000
	tv_timeout.tv_sec = timeout;
	tv_timeout.tv_usec = ((timeout - (float) tv_timeout.tv_sec) * USEC);

	if (fr_dict_global_init(autofree, dict_dir) < 0) {
		fr_perror("dhcpclient");
		exit(EXIT_FAILURE);
	}

	if (fr_dict_autoload(dhcpclient_dict) < 0) {
		fr_perror("dhcpclient");
		exit(EXIT_FAILURE);
	}

	if (fr_dict_attr_autoload(dhcpclient_dict_attr) < 0) {
		fr_perror("dhcpclient");
		exit(EXIT_FAILURE);
	}

	if (fr_dict_read(dict_freeradius, raddb_dir, FR_DICTIONARY_FILE) == -1) {
		fr_perror("dhcpclient");
		exit(EXIT_FAILURE);
	}
	fr_strerror();	/* Clear the error buffer */

	/*
	 *	Initialise the DHCPv4 library
	 */
	fr_dhcpv4_global_init();

	/*
	 *	Resolve hostname.
	 */
	server_ipaddr.af = AF_INET;
	if (strcmp(argv[1], "-") != 0) {
		if (fr_inet_pton_port(&server_ipaddr, &server_port, argv[1],
				      strlen(argv[1]), AF_UNSPEC, true, true) < 0) {
			fr_perror("dhcpclient");
			fr_exit_now(1);
		}
		client_ipaddr.af = server_ipaddr.af;
	}

	/*
	 *	See what kind of request we want to send.
	 */
	if (argc >= 3) {
		if (!isdigit((int) argv[2][0])) {
			packet_code = fr_str2int(request_types, argv[2], -2);
			if (packet_code == -2) {
				ERROR("Unknown packet type: %s", argv[2]);
				usage();
			}
		} else {
			packet_code = atoi(argv[2]);
		}
	}
	if (!server_port) server_port = 67;

	/*
	 *	set "raw mode" if an interface is specified and if destination
	 *	IP address is the broadcast address.
	 */
	if (iface) {
		iface_ind = if_nametoindex(iface);
		if (iface_ind <= 0) {
			ERROR("Unknown interface: %s", iface);
			exit(EXIT_FAILURE);
		}

		if (server_ipaddr.addr.v4.s_addr == 0xFFFFFFFF) {
			ERROR("Using interface: %s (index: %d) in raw packet mode", iface, iface_ind);
			raw_mode = true;
		}
	}

	request = request_init(filename);
	if (!request || !request->vps) {
		ERROR("Nothing to send");
		exit(EXIT_FAILURE);
	}

	/*
	 *	Set defaults if they weren't specified via pairs
	 */
	if (request->src_port == 0) request->src_port = server_port + 1;
	if (request->dst_port == 0) request->dst_port = server_port;
	if (request->src_ipaddr.af == AF_UNSPEC) request->src_ipaddr = client_ipaddr;
	if (request->dst_ipaddr.af == AF_UNSPEC) request->dst_ipaddr = server_ipaddr;
	if (!request->code) request->code = packet_code;

	/*
	 *	Sanity check.
	 */
	if (!request->code) {
		ERROR("Command was %s, and request did not contain DHCP-Message-Type nor Packet-Type",
		      (argc >= 3) ? "'auto'" : "unspecified");
		exit(EXIT_FAILURE);
	}

	/*
	 *	These kind of packets do not get a reply, so don't wait for one.
	 */
	if ((request->code == FR_DHCP_RELEASE) || (request->code == FR_DHCP_DECLINE)) {
		reply_expected = false;
	}

	/*
	 *	Encode the packet
	 */
	if (fr_dhcpv4_packet_encode(request) < 0) {
		ERROR("Failed encoding packet");
		exit(EXIT_FAILURE);
	}

	/*
	 *	Decode to produce VALUE_PAIRs from the default field
	 */
	if (fr_debug_lvl) {
		fr_dhcpv4_packet_decode(request);
		dhcp_packet_debug(request, false);
	}

#ifdef HAVE_LIBPCAP
	if (raw_mode) {
		ret = send_with_pcap(&reply, request);
	} else
#endif
	{
		ret = send_with_socket(&reply, request);
	}

	if (reply) {
		if (fr_dhcpv4_packet_decode(reply) < 0) {
			ERROR("Failed decoding packet");
			ret = -1;
		}
		dhcp_packet_debug(reply, true);
	}

	fr_dhcpv4_global_free();
	fr_dict_autofree(dhcpclient_dict);

	return ret < 0 ? 1 : 0;
}
예제 #2
0
int main(int argc, char **argv)
{
	int		c;
	char		filesecret[256];
	FILE		*fp;
	int		force_af = AF_UNSPEC;
	radsnmp_conf_t *conf;
	int		ret;
	int		sockfd;
	TALLOC_CTX	*autofree = talloc_autofree_context();

	fr_log_fp = stderr;

	conf = talloc_zero(autofree, radsnmp_conf_t);
	conf->proto = IPPROTO_UDP;
	conf->dict_dir = DICTDIR;
	conf->raddb_dir = RADDBDIR;
	conf->secret = talloc_strdup(conf, "testing123");
	conf->timeout.tv_sec = 3;
	conf->retries = 5;

#ifndef NDEBUG
	if (fr_fault_setup(autofree, getenv("PANIC_ACTION"), argv[0]) < 0) {
		fr_perror("radsnmp");
		exit(EXIT_FAILURE);
	}
#endif

	talloc_set_log_stderr();

	while ((c = getopt(argc, argv, "46c:d:D:f:Fhi:l:n:p:P:qr:sS:t:vx")) != -1) switch (c) {
		case '4':
			force_af = AF_INET;
			break;

		case '6':
			force_af = AF_INET6;
			break;

		case 'D':
			conf->dict_dir = optarg;
			break;

		case 'd':
			conf->raddb_dir = optarg;
			break;

		case 'l':
		{
			int log_fd;

			if (strcmp(optarg, "stderr") == 0) {
				fr_log_fp = stderr;	/* stdout goes to netsnmp */
				break;
			}

			log_fd = open(optarg, O_WRONLY | O_APPEND | O_CREAT, 0640);
			if (log_fd < 0) {
				fprintf(stderr, "radsnmp: Failed to open log file %s: %s\n",
					optarg, fr_syserror(errno));
				exit(EXIT_FAILURE);
			}
			fr_log_fp = fdopen(log_fd, "a");
		}
			break;

		case 'P':
			conf->proto_str = optarg;
			if (strcmp(conf->proto_str, "tcp") != 0) {
				if (strcmp(conf->proto_str, "udp") != 0) usage();
			} else {
				conf->proto = IPPROTO_TCP;
			}
			break;

		case 'r':
			if (!isdigit((int) *optarg)) usage();
			conf->retries = atoi(optarg);
			if ((conf->retries == 0) || (conf->retries > 1000)) usage();
			break;

		case 'S':
		{
			char *p;
			fp = fopen(optarg, "r");
			if (!fp) {
			       ERROR("Error opening %s: %s", optarg, fr_syserror(errno));
			       exit(EXIT_FAILURE);
			}
			if (fgets(filesecret, sizeof(filesecret), fp) == NULL) {
			       ERROR("Error reading %s: %s", optarg, fr_syserror(errno));
			       exit(EXIT_FAILURE);
			}
			fclose(fp);

			/* truncate newline */
			p = filesecret + strlen(filesecret) - 1;
			while ((p >= filesecret) &&
			      (*p < ' ')) {
			       *p = '\0';
			       --p;
			}

			if (strlen(filesecret) < 2) {
			       ERROR("Secret in %s is too short", optarg);
			       exit(EXIT_FAILURE);
			}
			talloc_free(conf->secret);
			conf->secret = talloc_strdup(conf, filesecret);
		}
		       break;

		case 't':
			if (fr_timeval_from_str(&conf->timeout, optarg) < 0) {
				ERROR("Failed parsing timeout value %s", fr_strerror());
				exit(EXIT_FAILURE);
			}
			break;

		case 'v':
			DEBUG("%s", radsnmp_version);
			exit(0);

		case 'x':
			fr_debug_lvl++;
			break;

		case 'h':
		default:
			usage();
	}
	argc -= (optind - 1);
	argv += (optind - 1);

	if ((argc < 2)  || ((conf->secret == NULL) && (argc < 3))) {
		ERROR("Insufficient arguments");
		usage();
	}
	/*
	 *	Mismatch between the binary and the libraries it depends on
	 */
	if (fr_check_lib_magic(RADIUSD_MAGIC_NUMBER) < 0) {
		fr_perror("radsnmp");
		return EXIT_FAILURE;
	}

	if (fr_dict_autoload(radsnmp_dict) < 0) {
		fr_perror("radsnmp");
		exit(EXIT_FAILURE);
	}

	if (fr_dict_attr_autoload(radsnmp_dict_attr) < 0) {
		fr_perror("radsnmp");
		exit(EXIT_FAILURE);
	}

	if (fr_dict_read(dict_freeradius, conf->raddb_dir, FR_DICTIONARY_FILE) == -1) {
		fr_perror("radsnmp");
		exit(EXIT_FAILURE);
	}
	fr_strerror();	/* Clear the error buffer */

	if (fr_log_fp) setvbuf(fr_log_fp, NULL, _IONBF, 0);

	/*
	 *	Get the request type
	 */
	if (!isdigit((int) argv[2][0])) {
		int code;

		code = fr_str2int(fr_request_types, argv[2], -1);
		if (code < 0) {
			ERROR("Unrecognised request type \"%s\"", argv[2]);
			usage();
		}
		conf->code = (unsigned int)code;
	} else {
		conf->code = atoi(argv[2]);
	}

	/*
	 *	Resolve hostname.
	 */
	if (fr_inet_pton_port(&conf->server_ipaddr, &conf->server_port, argv[1], -1, force_af, true, true) < 0) {
		ERROR("%s", fr_strerror());
		exit(EXIT_FAILURE);
	}

	/*
	 *	Add the secret
	 */
	if (argv[3]) {
		talloc_free(conf->secret);
		conf->secret = talloc_strdup(conf, argv[3]);
	}

	conf->snmp_root = fr_dict_attr_child_by_num(attr_vendor_specific, VENDORPEC_FREERADIUS);
	if (!conf->snmp_root) {
		ERROR("Incomplete dictionary: Missing definition for Extended-Attribute-1(%i)."
		      "Vendor-Specific(%i).FreeRADIUS(%i)",
		      attr_extended_attribute_1->attr,
		      attr_vendor_specific->attr,
		      VENDORPEC_FREERADIUS);
	dict_error:
		talloc_free(conf);
		exit(EXIT_FAILURE);
	}

	conf->snmp_oid_root = fr_dict_attr_child_by_num(conf->snmp_root, 1);
	if (!conf->snmp_oid_root) {
		ERROR("Incomplete dictionary: Missing definition for 1.Extended-Attribute-1(%i)."
		      "Vendor-Specific(%i).FreeRADIUS(%i).FreeRADIUS-Iso(%i)",
		      attr_extended_attribute_1->attr,
		      attr_vendor_specific->attr,
		      VENDORPEC_FREERADIUS, 1);
		goto dict_error;
	}

	switch (conf->proto) {
	case IPPROTO_TCP:
		sockfd = fr_socket_client_tcp(NULL, &conf->server_ipaddr, conf->server_port, true);
		break;

	default:
	case IPPROTO_UDP:
		sockfd = fr_socket_client_udp(NULL, NULL, &conf->server_ipaddr, conf->server_port, true);
		break;
	}
	if (sockfd < 0) {
		ERROR("Failed connecting to server %s:%hu", "foo", conf->server_port);
		ret = 1;
		goto finish;
	}

	fr_set_signal(SIGPIPE, rs_signal_stop);
	fr_set_signal(SIGINT, rs_signal_stop);
	fr_set_signal(SIGTERM, rs_signal_stop);
#ifdef SIGQUIT
	fr_set_signal(SIGQUIT, rs_signal_stop);
#endif

	DEBUG("%s - Starting pass_persist read loop", radsnmp_version);
	ret = radsnmp_send_recv(conf, sockfd);
	DEBUG("Read loop done");

finish:
	if (fr_log_fp) fflush(fr_log_fp);

	/*
	 *	Everything should be parented from conf
	 */
	talloc_free(conf);

	/*
	 *	...except the dictionaries
	 */
	fr_dict_autofree(radsnmp_dict);

	return ret;
}
예제 #3
0
int main(int argc, char **argv)
{
	int		c;
	char		const *radius_dir = RADDBDIR;
	char		const *dict_dir = DICTDIR;
	char		filesecret[256];
	FILE		*fp;
	int		do_summary = false;
	int		persec = 0;
	int		parallel = 1;
	rc_request_t	*this;
	int		force_af = AF_UNSPEC;
	fr_dict_t	*dict = NULL;

	/*
	 *	It's easier having two sets of flags to set the
	 *	verbosity of library calls and the verbosity of
	 *	radclient.
	 */
	fr_debug_lvl = 0;
	fr_log_fp = stdout;

#ifndef NDEBUG
	if (fr_fault_setup(getenv("PANIC_ACTION"), argv[0]) < 0) {
		fr_perror("radclient");
		exit(EXIT_FAILURE);
	}
#endif

	talloc_set_log_stderr();

	filename_tree = rbtree_create(NULL, filename_cmp, NULL, 0);
	if (!filename_tree) {
	oom:
		ERROR("Out of memory");
		exit(1);
	}

	while ((c = getopt(argc, argv, "46c:d:D:f:Fhi:n:p:qr:sS:t:vx"
#ifdef WITH_TCP
		"P:"
#endif
			   )) != EOF) switch (c) {
		case '4':
			force_af = AF_INET;
			break;

		case '6':
			force_af = AF_INET6;
			break;

		case 'c':
			if (!isdigit((int) *optarg))
				usage();
			resend_count = atoi(optarg);
			break;

		case 'D':
			dict_dir = optarg;
			break;

		case 'd':
			radius_dir = optarg;
			break;

		case 'f':
		{
			char const *p;
			rc_file_pair_t *files;

			files = talloc(talloc_autofree_context(), rc_file_pair_t);
			if (!files) goto oom;

			p = strchr(optarg, ':');
			if (p) {
				files->packets = talloc_strndup(files, optarg, p - optarg);
				if (!files->packets) goto oom;
				files->filters = p + 1;
			} else {
				files->packets = optarg;
				files->filters = NULL;
			}
			rbtree_insert(filename_tree, (void *) files);
		}
			break;

		case 'F':
			print_filename = true;
			break;

		case 'i':	/* currently broken */
			if (!isdigit((int) *optarg))
				usage();
			last_used_id = atoi(optarg);
			if ((last_used_id < 0) || (last_used_id > 255)) {
				usage();
			}
			break;

		case 'n':
			persec = atoi(optarg);
			if (persec <= 0) usage();
			break;

			/*
			 *	Note that sending MANY requests in
			 *	parallel can over-run the kernel
			 *	queues, and Linux will happily discard
			 *	packets.  So even if the server responds,
			 *	the client may not see the reply.
			 */
		case 'p':
			parallel = atoi(optarg);
			if (parallel <= 0) usage();
			break;

#ifdef WITH_TCP
		case 'P':
			proto = optarg;
			if (strcmp(proto, "tcp") != 0) {
				if (strcmp(proto, "udp") == 0) {
					proto = NULL;
				} else {
					usage();
				}
			} else {
				ipproto = IPPROTO_TCP;
			}
			break;

#endif

		case 'q':
			do_output = false;
			fr_log_fp = NULL; /* no output from you, either! */
			break;

		case 'r':
			if (!isdigit((int) *optarg)) usage();
			retries = atoi(optarg);
			if ((retries == 0) || (retries > 1000)) usage();
			break;

		case 's':
			do_summary = true;
			break;

		case 'S':
		{
			char *p;
			fp = fopen(optarg, "r");
			if (!fp) {
			       ERROR("Error opening %s: %s", optarg, fr_syserror(errno));
			       exit(1);
			}
			if (fgets(filesecret, sizeof(filesecret), fp) == NULL) {
			       ERROR("Error reading %s: %s", optarg, fr_syserror(errno));
			       exit(1);
			}
			fclose(fp);

			/* truncate newline */
			p = filesecret + strlen(filesecret) - 1;
			while ((p >= filesecret) &&
			      (*p < ' ')) {
			       *p = '\0';
			       --p;
			}

			if (strlen(filesecret) < 2) {
			       ERROR("Secret in %s is too short", optarg);
			       exit(1);
			}
			secret = filesecret;
		}
		       break;

		case 't':
			if (!isdigit((int) *optarg))
				usage();
			timeout = atof(optarg);
			break;

		case 'v':
			fr_debug_lvl = 1;
			DEBUG("%s", radclient_version);
			exit(0);

		case 'x':
			fr_debug_lvl++;
			break;

		case 'h':
		default:
			usage();
	}
	argc -= (optind - 1);
	argv += (optind - 1);

	if ((argc < 3)  || ((secret == NULL) && (argc < 4))) {
		ERROR("Insufficient arguments");
		usage();
	}
	/*
	 *	Mismatch between the binary and the libraries it depends on
	 */
	if (fr_check_lib_magic(RADIUSD_MAGIC_NUMBER) < 0) {
		fr_perror("radclient");
		return 1;
	}

	if (fr_dict_init(NULL, &dict, dict_dir, RADIUS_DICTIONARY, "radius") < 0) {
		fr_perror("radclient");
		return 1;
	}

	if (fr_dict_read(dict, radius_dir, RADIUS_DICTIONARY) == -1) {
		fr_perror("radclient");
		return 1;
	}
	fr_strerror();	/* Clear the error buffer */

	/*
	 *	Get the request type
	 */
	if (!isdigit((int) argv[2][0])) {
		packet_code = fr_str2int(fr_request_types, argv[2], -2);
		if (packet_code == -2) {
			ERROR("Unrecognised request type \"%s\"", argv[2]);
			usage();
		}
	} else {
		packet_code = atoi(argv[2]);
	}

	/*
	 *	Resolve hostname.
	 */
	if (strcmp(argv[1], "-") != 0) {
		if (fr_inet_pton_port(&server_ipaddr, &server_port, argv[1], -1, force_af, true, true) < 0) {
			ERROR("%s", fr_strerror());
			exit(1);
		}

		/*
		 *	Work backwards from the port to determine the packet type
		 */
		if (packet_code == PW_CODE_UNDEFINED) packet_code = radclient_get_code(server_port);
	}
	radclient_get_port(packet_code, &server_port);

	/*
	 *	Add the secret.
	 */
	if (argv[3]) secret = argv[3];

	/*
	 *	If no '-f' is specified, we're reading from stdin.
	 */
	if (rbtree_num_elements(filename_tree) == 0) {
		rc_file_pair_t *files;

		files = talloc_zero(talloc_autofree_context(), rc_file_pair_t);
		files->packets = "-";
		if (!radclient_init(files, files)) {
			exit(1);
		}
	}

	/*
	 *	Walk over the list of filenames, creating the requests.
	 */
	if (rbtree_walk(filename_tree, RBTREE_IN_ORDER, filename_walk, NULL) != 0) {
		ERROR("Failed parsing input files");
		exit(1);
	}

	/*
	 *	No packets read.  Die.
	 */
	if (!request_head) {
		ERROR("Nothing to send");
		exit(1);
	}

	/*
	 *	Bind to the first specified IP address and port.
	 *	This means we ignore later ones.
	 */
	if (request_head->packet->src_ipaddr.af == AF_UNSPEC) {
		memset(&client_ipaddr, 0, sizeof(client_ipaddr));
		client_ipaddr.af = server_ipaddr.af;
	} else {
		client_ipaddr = request_head->packet->src_ipaddr;
	}

	client_port = request_head->packet->src_port;

#ifdef WITH_TCP
	if (proto) {
		sockfd = fr_socket_client_tcp(NULL, &server_ipaddr, server_port, false);
	} else
#endif
	sockfd = fr_socket(&client_ipaddr, client_port);
	if (sockfd < 0) {
		ERROR("Error opening socket");
		exit(1);
	}

	pl = fr_packet_list_create(1);
	if (!pl) {
		ERROR("Out of memory");
		exit(1);
	}

	if (!fr_packet_list_socket_add(pl, sockfd, ipproto, &server_ipaddr,
				       server_port, NULL)) {
		ERROR("Out of memory");
		exit(1);
	}

	/*
	 *	Walk over the list of packets, sanity checking
	 *	everything.
	 */
	for (this = request_head; this != NULL; this = this->next) {
		this->packet->src_ipaddr = client_ipaddr;
		this->packet->src_port = client_port;
		if (radclient_sane(this) != 0) {
			exit(1);
		}
	}

	/*
	 *	Walk over the packets to send, until
	 *	we're all done.
	 *
	 *	FIXME: This currently busy-loops until it receives
	 *	all of the packets.  It should really have some sort of
	 *	send packet, get time to wait, select for time, etc.
	 *	loop.
	 */
	do {
		int n = parallel;
		rc_request_t *next;
		char const *filename = NULL;

		done = true;
		sleep_time = -1;

		/*
		 *	Walk over the packets, sending them.
		 */

		for (this = request_head; this != NULL; this = next) {
			next = this->next;

			/*
			 *	If there's a packet to receive,
			 *	receive it, but don't wait for a
			 *	packet.
			 */
			recv_one_packet(0);

			/*
			 *	This packet is done.  Delete it.
			 */
			if (this->done) {
				talloc_free(this);
				continue;
			}

			/*
			 *	Packets from multiple '-f' are sent
			 *	in parallel.
			 *
			 *	Packets from one file are sent in
			 *	series, unless '-p' is specified, in
			 *	which case N packets from each file
			 *	are sent in parallel.
			 */
			if (this->files->packets != filename) {
				filename = this->files->packets;
				n = parallel;
			}

			if (n > 0) {
				n--;

				/*
				 *	Send the current packet.
				 */
				if (send_one_packet(this) < 0) {
					talloc_free(this);
					break;
				}

				/*
				 *	Wait a little before sending
				 *	the next packet, if told to.
				 */
				if (persec) {
					struct timeval tv;

					/*
					 *	Don't sleep elsewhere.
					 */
					sleep_time = 0;

					if (persec == 1) {
						tv.tv_sec = 1;
						tv.tv_usec = 0;
					} else {
						tv.tv_sec = 0;
						tv.tv_usec = 1000000/persec;
					}

					/*
					 *	Sleep for milliseconds,
					 *	portably.
					 *
					 *	If we get an error or
					 *	a signal, treat it like
					 *	a normal timeout.
					 */
					select(0, NULL, NULL, NULL, &tv);
				}

				/*
				 *	If we haven't sent this packet
				 *	often enough, we're not done,
				 *	and we shouldn't sleep.
				 */
				if (this->resend < resend_count) {
					done = false;
					sleep_time = 0;
				}
			} else { /* haven't sent this packet, we're not done */
				assert(this->done == false);
				assert(this->reply == NULL);
				done = false;
			}
		}

		/*
		 *	Still have outstanding requests.
		 */
		if (fr_packet_list_num_elements(pl) > 0) {
			done = false;
		} else {
			sleep_time = 0;
		}

		/*
		 *	Nothing to do until we receive a request, so
		 *	sleep until then.  Once we receive one packet,
		 *	we go back, and walk through the whole list again,
		 *	sending more packets (if necessary), and updating
		 *	the sleep time.
		 */
		if (!done && (sleep_time > 0)) {
			recv_one_packet(sleep_time);
		}
	} while (!done);

	rbtree_free(filename_tree);
	fr_packet_list_free(pl);
	while (request_head) TALLOC_FREE(request_head);
	talloc_free(dict);

	if (do_summary) {
		DEBUG("Packet summary:\n"
		      "\tAccepted      : %" PRIu64 "\n"
		      "\tRejected      : %" PRIu64 "\n"
		      "\tLost          : %" PRIu64 "\n"
		      "\tPassed filter : %" PRIu64 "\n"
		      "\tFailed filter : %" PRIu64,
		      stats.accepted,
		      stats.rejected,
		      stats.lost,
		      stats.passed,
		      stats.failed
		);
	}

	if ((stats.lost > 0) || (stats.failed > 0)) {
		exit(1);
	}
	exit(0);
}