Exemplo n.º 1
0
/*
 *	Walk through the offset tree, operating on the cache
 */
static int offset_walk(void *context, void *data)
{
	offset_walk_t	*walk = context;
	NAS_PORT	*nas_port = data;
	struct radutmp	utmp;
	radutmp_simul_t *user, myUser;

	/*
	 *	Seek to the entry, and possibly re-write it.
	 */
	if (lseek(walk->fd, nas_port->offset, SEEK_SET) < 0) {
		rad_assert(0 == 1);
	}

	if (read(walk->fd, &utmp, sizeof(utmp)) != sizeof(utmp)) {
		rad_assert(0 == 1);
	}

	/*
	 *	If the entry in the file is NEWER than the reboot
	 *	packet, don't re-write it, and don't delete it.
	 */
	if (utmp.time > walk->now) {
		return 0;
	}

	utmp.type = P_IDLE;
	utmp.time = walk->now;

	if (lseek(walk->fd, -(off_t)sizeof(utmp), SEEK_CUR) < 0) {
		radlog(L_ERR, "rlm_radutmp: offset_walk: failed in lseek: %s",
		       strerror(errno));
		return 1;
	}

	write(walk->fd, &utmp, sizeof(utmp));

	strlcpy(myUser.login, utmp.login, sizeof(myUser.login));
	user = rbtree_finddata(walk->inst->user_tree, &myUser);
	rad_assert(user != NULL);
	rad_assert(user->simul_count > 0);
	user->simul_count--;
	if (user->simul_count == 0) {
		rbtree_deletebydata(walk->inst->user_tree, user);
	}

	if (rbtree_deletebydata(walk->cache->nas_ports, nas_port) == 0) {
		radlog(L_ERR, "rlm_radutmp: Failed to delete entry from cache");
		return 1;
	}

	/*
	 *	Insert the entry into the free list.
	 */
	nas_port->next = walk->cache->free_offsets;
	walk->cache->free_offsets = nas_port;

	return 0;
}
Exemplo n.º 2
0
/** Remove item from parent and fixup trees
 *
 * @param[in] parent	to remove child from.
 * @param[in] child	to remove.
 * @return
 *	- The item removed.
 *	- NULL if the item wasn't set.
 */
CONF_ITEM *cf_remove(CONF_ITEM *parent, CONF_ITEM *child)
{
	CONF_ITEM	*found;
	bool		in_ident1, in_ident2;

	if (!parent || !parent->child) return NULL;
	if (parent != child->parent) return NULL;

	for (found = fr_cursor_head(&parent->cursor);
	     found && (child != found);
	     found = fr_cursor_next(&parent->cursor));

	if (!found) return NULL;

	/*
	 *	Fixup the linked list
	 */
	found = fr_cursor_remove(&parent->cursor);
	if (!fr_cond_assert(found == child)) return NULL;

	in_ident1 = (rbtree_finddata(parent->ident1, child) == child);
	if (in_ident1 && (!rbtree_deletebydata(parent->ident1, child))) {
		rad_assert(0);
		return NULL;
	}

	in_ident2 = (rbtree_finddata(parent->ident2, child) == child);
	if (in_ident2 && (!rbtree_deletebydata(parent->ident2, child))) {
		rad_assert(0);
		return NULL;
	}

	/*
	 *	Look for twins
	 */
	for (found = fr_cursor_head(&parent->cursor);
	     found && (in_ident1 || in_ident2);
	     found = fr_cursor_next(&parent->cursor)) {
		if (in_ident1 && (_cf_ident1_cmp(found, child) == 0)) {
			rbtree_insert(parent->ident1, child);
			in_ident1 = false;
		}

		if (in_ident2 && (_cf_ident2_cmp(found, child) == 0)) {
			rbtree_insert(parent->ident2, child);
			in_ident2 = false;
		}
	}

	return child;
}
Exemplo n.º 3
0
void eap_handler_free(rlm_eap_t *inst, eap_handler_t *handler)
{
	if (!handler)
		return;

	if (handler->identity) {
		talloc_free(handler->identity);
		handler->identity = NULL;
	}

	if (handler->prev_eapds) eap_ds_free(&(handler->prev_eapds));
	if (handler->eap_ds) eap_ds_free(&(handler->eap_ds));

	if ((handler->opaque) && (handler->free_opaque)) {
		handler->free_opaque(handler->opaque);
		handler->opaque = NULL;
	}

	handler->opaque = NULL;
	handler->free_opaque = NULL;

	if (handler->certs) pairfree(&handler->certs);

	PTHREAD_MUTEX_LOCK(&(inst->handler_mutex));
	if (inst->handler_tree) {
		rbtree_deletebydata(inst->handler_tree, handler);
	}
	talloc_free(handler);
	PTHREAD_MUTEX_UNLOCK(&(inst->handler_mutex));
}
Exemplo n.º 4
0
void client_delete(RADCLIENT_LIST *clients, RADCLIENT *client)
{
	if (!client) return;

	if (!clients) clients = root_clients;

	if (!client->dynamic) return;

	rad_assert(client->ipaddr.prefix <= 128);

	client->dynamic = 2;	/* signal to client_free */

#ifdef WITH_STATS
	rbtree_deletebydata(tree_num, client);
#endif
	rbtree_deletebydata(clients->trees[client->ipaddr.prefix], client);
}
Exemplo n.º 5
0
/*
 *	Find a cached entry.
 */
static rlm_cache_entry_t *cache_find(rlm_cache_t *inst, REQUEST *request,
				     char const *key)
{
	int ttl;
	rlm_cache_entry_t *c, my_c;
	VALUE_PAIR *vp;

	/*
	 *	Look at the expiry heap.
	 */
	c = fr_heap_peek(inst->heap);
	if (!c) {
		rad_assert(rbtree_num_elements(inst->cache) == 0);
		return NULL;
	}

	/*
	 *	If it's time to expire an old entry, do so now.
	 */
	if (c->expires < request->timestamp) {
		fr_heap_extract(inst->heap, c);
		rbtree_deletebydata(inst->cache, c);
	}

	/*
	 *	Is there an entry for this key?
	 */
	my_c.key = key;
	c = rbtree_finddata(inst->cache, &my_c);
	if (!c) return NULL;

	/*
	 *	Yes, but it expired, OR the "forget all" epoch has
	 *	passed.  Delete it, and pretend it doesn't exist.
	 */
	if ((c->expires < request->timestamp) ||
	    (c->created < inst->epoch)) {
	delete:
		RDEBUG("Entry has expired, removing");

		fr_heap_extract(inst->heap, c);
		rbtree_deletebydata(inst->cache, c);

		return NULL;
	}
Exemplo n.º 6
0
static int _network_socket_free(fr_network_socket_t *s)
{
	fr_network_t *nr = s->nr;
	fr_channel_data_t *cd;

	if (!s->dead) {
		if (fr_event_fd_delete(nr->el, s->fd, s->filter) < 0) {
			PERROR("Failed deleting socket from event loop in _network_socket_free");
			rad_assert("Failed removing socket FD from event loop in _network_socket_free" == NULL);
		}
	}

	rbtree_deletebydata(nr->sockets, s);
	rbtree_deletebydata(nr->sockets_by_num, s);

	if (s->listen->app_io->close) {
		s->listen->app_io->close(s->listen->app_io_instance);
	} else {
		close(s->fd);
	}

	if (s->pending) {
		fr_message_done(&s->pending->m);
		s->pending = NULL;
	}

	/*
	 *	Clean up any queued entries.
	 */
	while ((cd = fr_heap_pop(s->waiting)) != NULL) {
		fr_message_done(&cd->m);
	}

	talloc_free(s->waiting);

	return 0;
}
Exemplo n.º 7
0
/** Unregister a map processor
 *
 * @param[in] proc to unregister.
 */
static int _map_proc_unregister(map_proc_t *proc)
{
	map_proc_t find;
	map_proc_t *found;

	strlcpy(find.name, proc->name, sizeof(find.name));
	find.length = strlen(find.name);

	found = rbtree_finddata(map_proc_root, &find);
	if (!found) return 0;

	rbtree_deletebydata(map_proc_root, found);

	return 0;
}
Exemplo n.º 8
0
/** Unregister an xlat function
 *
 * We can only have one function to call per name, so the passing of "func"
 * here is extraneous.
 *
 * @param[in] name xlat to unregister.
 * @param[in] func unused.
 * @param[in] instance data.
 */
void xlat_unregister(char const *name, UNUSED RAD_XLAT_FUNC func, void *instance)
{
	xlat_t	*c;
	xlat_t		my_xlat;

	if (!name) return;

	strlcpy(my_xlat.name, name, sizeof(my_xlat.name));
	my_xlat.length = strlen(my_xlat.name);

	c = rbtree_finddata(xlat_root, &my_xlat);
	if (!c) return;

	if (c->instance != instance) return;

	rbtree_deletebydata(xlat_root, c);
}
Exemplo n.º 9
0
/**
 * @brief Unregister an xlat function.
 *
 *	We can only have one function to call per name, so the
 *	passing of "func" here is extraneous.
 *
 * @param module xlat to unregister
 * @param func Unused
 * @return Void.
 */
void xlat_unregister(const char *module, RAD_XLAT_FUNC func, void *instance)
{
	xlat_t	*c;
	xlat_t		my_xlat;

	func = func;		/* -Wunused */

	if (!module) return;

	strlcpy(my_xlat.module, module, sizeof(my_xlat.module));
	my_xlat.length = strlen(my_xlat.module);

	c = rbtree_finddata(xlat_root, &my_xlat);
	if (!c) return;

	if (c->instance != instance) return;

	rbtree_deletebydata(xlat_root, c);
}
Exemplo n.º 10
0
static int filter_packet(RADIUS_PACKET *packet)
{
	VALUE_PAIR *check_item;
	VALUE_PAIR *vp;
	unsigned int pass, fail;
	int compare;

	pass = fail = 0;
	for (vp = packet->vps; vp != NULL; vp = vp->next) {
		for (check_item = filter_vps;
		     check_item != NULL;
		     check_item = check_item->next)
			if ((check_item->da == vp->da)
			 && (check_item->op != T_OP_SET)) {
				compare = paircmp(check_item, vp);
				if (compare == 1)
					pass++;
				else
					fail++;
			}
	}

	if (fail == 0 && pass != 0) {
		/*
		 *	Cache authentication requests, as the replies
		 *	may not match the RADIUS filter.
		 */
		if ((packet->code == PW_AUTHENTICATION_REQUEST) ||
		    (packet->code == PW_ACCOUNTING_REQUEST)) {
			rbtree_deletebydata(filter_tree, packet);
			
			if (!rbtree_insert(filter_tree, packet)) {
			oom:
				fprintf(stderr, "radsniff: Out of memory\n");
				exit(1);
			}
		}
		return 0;	/* matched */
	}

	/*
	 *	Don't create erroneous matches.
	 */
	if ((packet->code == PW_AUTHENTICATION_REQUEST) ||
	    (packet->code == PW_ACCOUNTING_REQUEST)) {
		rbtree_deletebydata(filter_tree, packet);
		return 1;
	}
	
	/*
	 *	Else see if a previous Access-Request
	 *	matched.  If so, also print out the
	 *	matching accept, reject, or challenge.
	 */
	if ((packet->code == PW_AUTHENTICATION_ACK) ||
	    (packet->code == PW_AUTHENTICATION_REJECT) ||
	    (packet->code == PW_ACCESS_CHALLENGE) ||
	    (packet->code == PW_ACCOUNTING_RESPONSE)) {
		RADIUS_PACKET *reply;

		/*
		 *	This swaps the various fields.
		 */
		reply = rad_alloc_reply(NULL, packet);
		if (!reply) goto oom;
		
		compare = 1;
		if (rbtree_finddata(filter_tree, reply)) {
			compare = 0;
		}
		
		rad_free(&reply);
		return compare;
	}
	
	return 1;
}
Exemplo n.º 11
0
static void got_packet(UNUSED uint8_t *args, const struct pcap_pkthdr *header, const uint8_t *data)
{

	static int count = 1;			/* Packets seen */
	
	/*
	 *  Define pointers for packet's attributes
	 */
	const struct ip_header *ip;		/* The IP header */
	const struct udp_header *udp;		/* The UDP header */
	const uint8_t *payload;			/* Packet payload */
	
	/*
	 *  And define the size of the structures we're using
	 */
	int size_ethernet = sizeof(struct ethernet_header);
	int size_ip = sizeof(struct ip_header);
	int size_udp = sizeof(struct udp_header);
	
	/*
	 *  For FreeRADIUS
	 */
	RADIUS_PACKET *packet, *original;
	struct timeval elapsed;

	/*
	 * Define our packet's attributes
	 */

	if ((data[0] == 2) && (data[1] == 0) &&
	    (data[2] == 0) && (data[3] == 0)) {
		ip = (const struct ip_header*) (data + 4);

	} else {
		ip = (const struct ip_header*)(data + size_ethernet);
	}
	
	udp = (const struct udp_header*)(((const uint8_t *) ip) + size_ip);
	payload = (const uint8_t *)(((const uint8_t *) udp) + size_udp);

	packet = rad_alloc(NULL, 0);
	if (!packet) {
		fprintf(stderr, "Out of memory\n");
		return;
	}

	packet->src_ipaddr.af = AF_INET;
	packet->src_ipaddr.ipaddr.ip4addr.s_addr = ip->ip_src.s_addr;
	packet->src_port = ntohs(udp->udp_sport);
	packet->dst_ipaddr.af = AF_INET;
	packet->dst_ipaddr.ipaddr.ip4addr.s_addr = ip->ip_dst.s_addr;
	packet->dst_port = ntohs(udp->udp_dport);

	memcpy(&packet->data, &payload, sizeof(packet->data));
	packet->data_len = header->len - (payload - data);

	if (!rad_packet_ok(packet, 0)) {
		DEBUG(log_dst, "Packet: %s\n", fr_strerror());
		
		DEBUG(log_dst, "  From     %s:%d\n", inet_ntoa(ip->ip_src), ntohs(udp->udp_sport));
		DEBUG(log_dst, "  To:      %s:%d\n", inet_ntoa(ip->ip_dst), ntohs(udp->udp_dport));
		DEBUG(log_dst, "  Type:    %s\n", fr_packet_codes[packet->code]);

		rad_free(&packet);
		return;
	}
	
	switch (packet->code) {
	case PW_COA_REQUEST:
		/* we need a 16 x 0 byte vector for decrypting encrypted VSAs */
		original = nullpacket;
		break;
	case PW_AUTHENTICATION_ACK:
		/* look for a matching request and use it for decoding */
		original = rbtree_finddata(request_tree, packet);
		break;
	case PW_AUTHENTICATION_REQUEST:
		/* save the request for later matching */
		original = rad_alloc_reply(NULL, packet);
		if (original) { /* just ignore allocation failures */
			rbtree_deletebydata(request_tree, original);
			rbtree_insert(request_tree, original);
		}
		/* fallthrough */
	default:
		/* don't attempt to decode any encrypted attributes */
		original = NULL;
	}

	/*
	 *  Decode the data without bothering to check the signatures.
	 */
	if (rad_decode(packet, original, radius_secret) != 0) {
		rad_free(&packet);
		fr_perror("decode");
		return;
	}

	/*
	 *  We've seen a successfull reply to this, so delete it now
	 */
	if (original)
		rbtree_deletebydata(request_tree, original);

	if (filter_vps && filter_packet(packet)) {
		rad_free(&packet);
		DEBUG(log_dst, "Packet number %d doesn't match\n", count++);
		return;
	}

	if (out) {
		pcap_dump((void *) out, header, data);
		goto check_filter;
	}

	INFO(log_dst, "%s Id %d\t", fr_packet_codes[packet->code], packet->id);

	/*
	 *  Print the RADIUS packet
	 */
	INFO(log_dst, "%s:%d -> ", inet_ntoa(ip->ip_src), ntohs(udp->udp_sport));
	INFO(log_dst, "%s:%d", inet_ntoa(ip->ip_dst), ntohs(udp->udp_dport));
	
	DEBUG1(log_dst, "\t(%d packets)", count++);
	
	if (!start_pcap.tv_sec) {
		start_pcap = header->ts;
	}

	tv_sub(&header->ts, &start_pcap, &elapsed);

	INFO(log_dst, "\t+%u.%03u", (unsigned int) elapsed.tv_sec,
	       (unsigned int) elapsed.tv_usec / 1000);
	
	if (fr_debug_flag > 1) {
		DEBUG(log_dst, "\n");
		if (packet->vps) {
			if (do_sort) sort(packet);
	
			vp_printlist(log_dst, packet->vps);
			pairfree(&packet->vps);
		}
	}
	
	INFO(log_dst, "\n");
	
	if (!to_stdout && (fr_debug_flag > 4)) {
		rad_print_hex(packet);
	}
	
	fflush(log_dst);

 check_filter:
	/*
	 *  If we're doing filtering, Access-Requests are cached in the
	 *  filter tree.
	 */
	if (!filter_vps ||
	    ((packet->code != PW_AUTHENTICATION_REQUEST) &&
	     (packet->code != PW_ACCOUNTING_REQUEST))) {
		rad_free(&packet);
	}
}
Exemplo n.º 12
0
static void rs_process_packet(rs_event_t *event, struct pcap_pkthdr const *header, uint8_t const *data)
{

	static int count = 0;			/* Packets seen */
	rs_stats_t *stats = event->stats;
	decode_fail_t reason;

	/*
	 *	Pointers into the packet data we just received
	 */
	size_t len;
	uint8_t const		*p = data;
	struct ip_header const	*ip = NULL;	/* The IP header */
	struct ip_header6 const	*ip6 = NULL;	/* The IPv6 header */
	struct udp_header const	*udp;		/* The UDP header */
	uint8_t			version;	/* IP header version */
	bool			response = false;	/* Was it a response code */

	RADIUS_PACKET *current, *original;
	struct timeval elapsed;
	struct timeval latency;

	count++;

	if (header->caplen <= 5) {
		INFO("Packet too small, captured %i bytes", header->caplen);
		return;
	}

	/*
	 *	Loopback header
	 */
	if ((p[0] == 2) && (p[1] == 0) && (p[2] == 0) && (p[3] == 0)) {
		p += 4;
	/*
	 *	Ethernet header
	 */
	} else {
		p += sizeof(struct ethernet_header);
	}

	version = (p[0] & 0xf0) >> 4;
	switch (version) {
	case 4:
		ip = (struct ip_header const *)p;
		len = (0x0f & ip->ip_vhl) * 4;	/* ip_hl specifies length in 32bit words */
		p += len;
		break;

	case 6:
		ip6 = (struct ip_header6 const *)p;
		p += sizeof(struct ip_header6);

		break;

	default:
		DEBUG("IP version invalid %i", version);
		return;
	}

	/*
	 *	End of variable length bits, do basic check now to see if packet looks long enough
	 */
	len = (p - data) + sizeof(struct udp_header) + (sizeof(radius_packet_t) - 1);	/* length value */
	if (len > header->caplen) {
		DEBUG("Packet too small, we require at least %zu bytes, captured %i bytes",
		      (size_t) len, header->caplen);
		return;
	}

	udp = (struct udp_header const *)p;
	p += sizeof(struct udp_header);

	/*
	 *	With artificial talloc memory limits there's a good chance we can
	 *	recover once some requests timeout, so make an effort to deal
	 *	with allocation failures gracefully.
	 */
	current = rad_alloc(NULL, 0);
	if (!current) {
		ERROR("Failed allocating memory to hold decoded packet");
		return;
	}
	current->timestamp = header->ts;
	current->data_len = header->caplen - (data - p);
	memcpy(&current->data, &p, sizeof(current->data));

	/*
	 *	Populate IP/UDP fields from PCAP data
	 */
	if (ip) {
		current->src_ipaddr.af = AF_INET;
		current->src_ipaddr.ipaddr.ip4addr.s_addr = ip->ip_src.s_addr;

		current->dst_ipaddr.af = AF_INET;
		current->dst_ipaddr.ipaddr.ip4addr.s_addr = ip->ip_dst.s_addr;
	} else {
		current->src_ipaddr.af = AF_INET6;
		memcpy(&current->src_ipaddr.ipaddr.ip6addr.s6_addr, &ip6->ip_src.s6_addr,
		       sizeof(current->src_ipaddr.ipaddr.ip6addr.s6_addr));

		current->dst_ipaddr.af = AF_INET6;
		memcpy(&current->dst_ipaddr.ipaddr.ip6addr.s6_addr, &ip6->ip_dst.s6_addr,
		       sizeof(current->dst_ipaddr.ipaddr.ip6addr.s6_addr));
	}

	current->src_port = ntohs(udp->udp_sport);
	current->dst_port = ntohs(udp->udp_dport);

	if (!rad_packet_ok(current, 0, &reason)) {
		DEBUG("(%i) ** %s **", count, fr_strerror());

		DEBUG("(%i) %s Id %i %s:%s:%d -> %s:%d\t+%u.%03u", count,
		      fr_packet_codes[current->code], current->id,
		      event->in->name,
		      fr_inet_ntop(current->src_ipaddr.af, &current->src_ipaddr.ipaddr), current->src_port,
		      fr_inet_ntop(current->dst_ipaddr.af, &current->dst_ipaddr.ipaddr), current->dst_port,
		      (unsigned int) elapsed.tv_sec, ((unsigned int) elapsed.tv_usec / 1000));

		rad_free(&current);
		return;
	}

	switch (current->code) {
	case PW_CODE_COA_REQUEST:
		/* we need a 16 x 0 byte vector for decrypting encrypted VSAs */
		original = nullpacket;
		break;
	case PW_CODE_ACCOUNTING_RESPONSE:
	case PW_CODE_AUTHENTICATION_REJECT:
	case PW_CODE_AUTHENTICATION_ACK:
		response = true;
		/* look for a matching request and use it for decoding */
		original = rbtree_finddata(request_tree, current);
		break;
	case PW_CODE_ACCOUNTING_REQUEST:
	case PW_CODE_AUTHENTICATION_REQUEST:
		/* save the request for later matching */
		original = rad_alloc_reply(event->conf, current);
		original->timestamp = header->ts;
		if (original) { /* just ignore allocation failures */
			rbtree_deletebydata(request_tree, original);
			rbtree_insert(request_tree, original);
		}
		/* fallthrough */
	default:
		/* don't attempt to decode any encrypted attributes */
		original = NULL;
	}

	/*
	 *  Decode the data without bothering to check the signatures.
	 */
	if (rad_decode(current, original, event->conf->radius_secret) != 0) {
		rad_free(&current);
		fr_perror("decode");
		return;
	}

	if (filter_vps && rs_filter_packet(current)) {
		rad_free(&current);
		DEBUG("Packet number %d doesn't match", count++);
		return;
	}

	if (event->out) {
		pcap_dump((void *) (event->out->dumper), header, data);
		goto check_filter;
	}

	rs_tv_sub(&header->ts, &start_pcap, &elapsed);

	rs_stats_update_count(&stats->gauge, current);
	if (original) {
		rs_tv_sub(&current->timestamp, &original->timestamp, &latency);

		/*
		 *	Update stats for both the request and response types.
		 *
		 *	This isn't useful for things like Access-Requests, but will be useful for
		 *	CoA and Disconnect Messages, as we get the average latency across both
		 *	response types.
		 *
		 *	It also justifies allocating 255 instances rs_latency_t.
		 */
		rs_stats_update_latency(&stats->exchange[current->code], &latency);
		rs_stats_update_latency(&stats->exchange[original->code], &latency);

		/*
		 *	Print info about the response.
		 */
		DEBUG("(%i) %s Id %i %s:%s:%d %s %s:%d\t+%u.%03u\t+%u.%03u", count,
		      fr_packet_codes[current->code], current->id,
		      event->in->name,
		      fr_inet_ntop(current->src_ipaddr.af, &current->src_ipaddr.ipaddr), current->src_port,
		      response ? "<-" : "->",
		      fr_inet_ntop(current->dst_ipaddr.af, &current->dst_ipaddr.ipaddr), current->dst_port,
		      (unsigned int) elapsed.tv_sec, ((unsigned int) elapsed.tv_usec / 1000),
		      (unsigned int) latency.tv_sec, ((unsigned int) latency.tv_usec / 1000));
	/*
	 *	It's the original request
	 */
	} else {
		/*
		 *	Print info about the request
		 */
		DEBUG("(%i) %s Id %i %s:%s:%d %s %s:%d\t+%u.%03u", count,
		      fr_packet_codes[current->code], current->id,
		      event->in->name,
		      fr_inet_ntop(current->src_ipaddr.af, &current->src_ipaddr.ipaddr), current->src_port,
		      response ? "<-" : "->",
		      fr_inet_ntop(current->dst_ipaddr.af, &current->dst_ipaddr.ipaddr), current->dst_port,
		      (unsigned int) elapsed.tv_sec, ((unsigned int) elapsed.tv_usec / 1000));
	}

	if (fr_debug_flag > 1) {
		if (current->vps) {
			if (event->conf->do_sort) {
				pairsort(&current->vps, true);
			}
			vp_printlist(log_dst, current->vps);
			pairfree(&current->vps);
		}
	}

	/*
	 *  We've seen a successful reply to this, so delete it now
	 */
	if (original) {
		rbtree_deletebydata(request_tree, original);
	}

	if (!event->conf->to_stdout && (fr_debug_flag > 4)) {
		rad_print_hex(current);
	}

	fflush(log_dst);

check_filter:
	/*
	 *  If we're doing filtering, Access-Requests are cached in the
	 *  filter tree.
	 */
	if (!filter_vps ||
	    ((current->code != PW_CODE_AUTHENTICATION_REQUEST) && (current->code != PW_CODE_ACCOUNTING_REQUEST))) {
		rad_free(&current);
	}
}
Exemplo n.º 13
0
int fr_packet_list_delete(fr_packet_list_t* pl,
                    RADIUS_PACKET* request)
{
    if(!pl || !request) return -1;
    return (rbtree_deletebydata(pl->tree,&request) == 0 ?  -1 : 0);
}
Exemplo n.º 14
0
/*
 *	Store logins in the RADIUS utmp file.
 */
static rlm_rcode_t radutmp_accounting(void *instance, REQUEST *request)
{
	rlm_radutmp_t	*inst = instance;
	struct radutmp	utmp, u;
	VALUE_PAIR	*vp;
	int		status = -1;
	uint32_t	nas_address = 0;
	uint32_t	framed_address = 0;
	int		protocol = -1;
	int		fd;
	int		port_seen = 0;
	char		buffer[256];
	char		filename[1024];
	char		ip_name[32]; /* 255.255.255.255 */
	const char	*nas;
	NAS_PORT	*nas_port, myPort;
	radutmp_cache_t *cache;
	int		read_size;
	rbnode_t	*node;

	/*
	 *	Which type is this.
	 */
	if ((vp = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE, 0, TAG_ANY)) == NULL) {
		radlog(L_ERR, "rlm_radutmp: No Accounting-Status-Type record.");
		return RLM_MODULE_NOOP;
	}
	status = vp->vp_integer;

	/*
	 *	Look for weird reboot packets.
	 *
	 *	ComOS (up to and including 3.5.1b20) does not send
	 *	standard PW_STATUS_ACCOUNTING_* messages.
	 *
	 *	Check for:  o no Acct-Session-Time, or time of 0
	 *		    o Acct-Session-Id of "00000000".
	 *
	 *	We could also check for NAS-Port, that attribute
	 *	should NOT be present (but we don't right now).
	 */
	if ((status != PW_STATUS_ACCOUNTING_ON) &&
	    (status != PW_STATUS_ACCOUNTING_OFF)) do {
		int check1 = 0;
		int check2 = 0;

		if ((vp = pairfind(request->packet->vps, PW_ACCT_SESSION_TIME, 0, TAG_ANY))
		     == NULL || vp->vp_date == 0)
			check1 = 1;
		if ((vp = pairfind(request->packet->vps, PW_ACCT_SESSION_ID, 0, TAG_ANY))
		     != NULL && vp->length == 8 &&
		     memcmp(vp->vp_strvalue, "00000000", 8) == 0)
			check2 = 1;
		if (check1 == 0 || check2 == 0) {
#if 0 /* Cisco sometimes sends START records without username. */
			radlog(L_ERR, "rlm_radutmp: no username in record");
			return RLM_MODULE_FAIL;
#else
			break;
#endif
		}
		radlog(L_INFO, "rlm_radutmp: converting reboot records.");
		if (status == PW_STATUS_STOP)
			status = PW_STATUS_ACCOUNTING_OFF;
		if (status == PW_STATUS_START)
			status = PW_STATUS_ACCOUNTING_ON;
	} while(0);

	memset(&utmp, 0, sizeof(utmp));
	utmp.porttype = 'A';

	/*
	 *	First, find the interesting attributes.
	 */
	for (vp = request->packet->vps; vp; vp = vp->next) {
		switch (vp->da->attribute) {
			case PW_LOGIN_IP_HOST:
			case PW_FRAMED_IP_ADDRESS:
				framed_address = vp->vp_ipaddr;
				utmp.framed_address = vp->vp_ipaddr;
				break;
			case PW_FRAMED_PROTOCOL:
				protocol = vp->vp_integer;
				break;
			case PW_NAS_IP_ADDRESS:
				nas_address = vp->vp_ipaddr;
				utmp.nas_address = vp->vp_ipaddr;
				break;
			case PW_NAS_PORT:
				utmp.nas_port = vp->vp_integer;
				port_seen = 1;
				break;
			case PW_ACCT_DELAY_TIME:
				utmp.delay = vp->vp_integer;
				break;
			case PW_ACCT_SESSION_ID:
				/*
				 *	If it's too big, only use the
				 *	last bit.
				 */
				if (vp->length > sizeof(utmp.session_id)) {
					int length = vp->length - sizeof(utmp.session_id);

					/*
					 * 	Ascend is br0ken - it
					 * 	adds a \0 to the end
					 * 	of any string.
					 * 	Compensate.
					 */
					if (vp->vp_strvalue[vp->length - 1] == 0) {
						length--;
					}

					memcpy(utmp.session_id,
					      vp->vp_strvalue + length,
					      sizeof(utmp.session_id));
				} else {
					memset(utmp.session_id, 0,
					       sizeof(utmp.session_id));
					memcpy(utmp.session_id,
					       vp->vp_strvalue,
					       vp->length);
				}
				break;
			case PW_NAS_PORT_TYPE:
				if (vp->vp_integer <= 4)
					utmp.porttype = porttypes[vp->vp_integer];
				break;
			case PW_CALLING_STATION_ID:
				if(inst->callerid_ok)
					strlcpy(utmp.caller_id,
						(char *)vp->vp_strvalue,
						sizeof(utmp.caller_id));
				break;
		}
	}

	/*
	 *	If we didn't find out the NAS address, use the
	 *	originator's IP address.
	 */
	if (nas_address == 0) {
		nas_address = request->packet->src_ipaddr;
		utmp.nas_address = nas_address;
		nas = request->client->shortname;

	} else if (request->packet->src_ipaddr.ipaddr.ip4addr.s_addr == nas_address) {		/* might be a client, might not be. */
		nas = request->client->shortname;

	} else {
		/*
		 *	The NAS isn't a client, it's behind
		 *	a proxy server.  In that case, just
		 *	get the IP address.
		 */
		nas = ip_ntoa(ip_name, nas_address);
	}


	/*
	 *	Set the protocol field.
	 */
	if (protocol == PW_PPP)
		utmp.proto = 'P';
	else if (protocol == PW_SLIP)
		utmp.proto = 'S';
	else
		utmp.proto = 'T';

	utmp.time = request->timestamp - utmp.delay;

	/*
	 *	Get the utmp filename, via xlat.
	 */
	radius_xlat(filename, sizeof(filename), inst->filename, request, NULL);

	/*
	 *	Future: look up filename in filename tree, to get
	 *	radutmp_cache_t pointer
	 */
	cache = &inst->cache;

	/*
	 *	For now, double-check the filename, to be sure it isn't
	 *	changing.
	 */
	if (!cache->filename) {
		cache->filename = strdup(filename);
		rad_assert(cache->filename != NULL);

	} else if (strcmp(cache->filename, filename) != 0) {
		radlog(L_ERR, "rlm_radutmp: We do not support dynamically named files.");
		return RLM_MODULE_FAIL;
	}

	/*
	 *	If the lookup failed, create a new one, and add it
	 *	to the filename tree, and cache the file, as below.
	 */

	/*
	 *	For aging, in the future.
	 */
	cache->last_used = request->timestamp;

	/*
	 *	If we haven't already read the file, then read the
	 *	entire file, in order to cache its entries.
	 */
	if (!cache->cached_file) {
		cache_file(inst, cache);
	}

	/*
	 *	See if this was a reboot.
	 *
	 *	Hmm... we may not want to zap all of the users when
	 *	the NAS comes up, because of issues with receiving
	 *	UDP packets out of order.
	 */
	if (status == PW_STATUS_ACCOUNTING_ON && nas_address) {
		radlog(L_INFO, "rlm_radutmp: NAS %s restarted (Accounting-On packet seen)",
		       nas);
		if (!radutmp_zap(inst, cache, nas_address, utmp.time)) {
			rad_assert(0 == 1);
		}
		return RLM_MODULE_OK;
	}

	if (status == PW_STATUS_ACCOUNTING_OFF && nas_address) {
		radlog(L_INFO, "rlm_radutmp: NAS %s rebooted (Accounting-Off packet seen)",
		       nas);
		if (!radutmp_zap(inst, cache, nas_address, utmp.time)) {
			rad_assert(0 == 1);
		}
		return RLM_MODULE_OK;
	}

	/*
	 *	If we don't know this type of entry, then pretend we
	 *	succeeded.
	 */
	if (status != PW_STATUS_START &&
	    status != PW_STATUS_STOP &&
	    status != PW_STATUS_ALIVE) {
		radlog(L_ERR, "rlm_radutmp: NAS %s port %u unknown packet type %d, ignoring it.",
		       nas, utmp.nas_port, status);
		return RLM_MODULE_NOOP;
	}

	/*
	 *	Perhaps we don't want to store this record into
	 *	radutmp. We skip records:
	 *
	 *	- without a NAS-Port (telnet / tcp access)
	 *	- with the username "!root" (console admin login)
	 */
	if (!port_seen) {
		DEBUG2("  rlm_radutmp: No NAS-Port in the packet.  Cannot do anything.");
		DEBUG2("  rlm_radumtp: WARNING: checkrad will probably not work!");
		return RLM_MODULE_NOOP;
	}

	/*
	 *	Translate the User-Name attribute, or whatever else
	 *	they told us to use.
	 */
	*buffer = '\0';
	radius_xlat(buffer, sizeof(buffer), inst->username, request, NULL);

	/*
	 *	Don't log certain things...
	 */
	if (strcmp(buffer, "!root") == 0) {
		DEBUG2("  rlm_radutmp: Not recording administrative user");

		return RLM_MODULE_NOOP;
	}
	strlcpy(utmp.login, buffer, RUT_NAMESIZE);

	/*
	 *	First, try to open the file.  If it doesn't exist,
	 *	nuke the existing caches, and try to create it.
	 *
	 *	FIXME: Create any intermediate directories, as
	 *	appropriate.  See rlm_detail.
	 */
	fd = open(cache->filename, O_RDWR, inst->permission);
	if (fd < 0) {
		if (errno == ENOENT) {
			DEBUG2("  rlm_radutmp: File %s doesn't exist, creating it.", cache->filename);
			if (!cache_reset(inst, cache)) return RLM_MODULE_FAIL;

			/*
			 *	Try to create the file.
			 */
			fd = open(cache->filename, O_RDWR | O_CREAT,
				  inst->permission);
		}
	} else {		/* exists, but may be empty */
		struct stat buf;

		/*
		 *	If the file is empty, reset the cache.
		 */
		if ((stat(cache->filename, &buf) == 0) &&
		    (buf.st_size == 0) &&
		    (!cache_reset(inst, cache))) {
			return RLM_MODULE_FAIL;
		}
		DEBUG2("  rlm_radutmp: File %s was truncated.  Resetting cache.",
		       cache->filename);
	}

	/*
	 *	Error from creation, or error other than ENOENT: die.
	 */
	if (fd < 0) {
		radlog(L_ERR, "rlm_radutmp: Error accessing file %s: %s",
		       cache->filename, strerror(errno));
		return RLM_MODULE_FAIL;
	}

	/*
	 *	OK.  Now that we've prepared everything we want to do,
	 *	let's see if we've cached the entry.
	 */
	myPort.nas_address = utmp.nas_address;
	myPort.nas_port = utmp.nas_port;

	pthread_mutex_lock(&cache->mutex);
	node = rbtree_find(cache->nas_ports, &myPort);
	pthread_mutex_unlock(&cache->mutex);

	if (node) {
		nas_port = rbtree_node2data(cache->nas_ports, node);
#if 0

		/*
		 *	stat the file, and get excited if it's been
		 *	truncated.
		 *
		 *	i.e wipe out the cache, and re-read the file.
		 */

		/*
		 *	Now find the new entry.
		 */
		pthread_mutex_lock(&cache->mutex);
		node = rbtree_find(cache->nas_ports, &myPort);
		pthread_mutex_unlock(&cache->mutex);
#endif
	}

	if (!node) {
		radutmp_simul_t *user;

		/*
		 *	Not found in the cache, and we're trying to
		 *	delete an existing record: ignore it.
		 */
		if (status == PW_STATUS_STOP) {
			DEBUG2("  rlm_radumtp: Logout entry for NAS %s port %u with no Login: ignoring it.",
			       nas, utmp.nas_port);
			return RLM_MODULE_NOOP;
		}

		pthread_mutex_lock(&cache->mutex);

		/*
		 *	It's a START or ALIVE.  Try to find a free
		 *	offset where we can store the new entry, or
		 *	create one, if one doesn't already exist.
		 */
		if (!cache->free_offsets) {
			cache->free_offsets = rad_malloc(sizeof(NAS_PORT));
			memset(cache->free_offsets, 0,
			       sizeof(*(cache->free_offsets)));
			cache->free_offsets->offset = cache->max_offset;
			cache->max_offset += sizeof(u);
		}

		/*
		 *	Grab the offset, and put it into the various
		 *	caches.
		 */
		nas_port = cache->free_offsets;
		cache->free_offsets = nas_port->next;

		nas_port->nas_address = nas_address;
		nas_port->nas_port = utmp.nas_port;

		if (!rbtree_insert(cache->nas_ports, nas_port)) {
			rad_assert(0 == 1);
		}

		/*
		 *	Allocate new entry, and add it
		 *	to the tree.
		 */
		user = rad_malloc(sizeof(user));
		strlcpy(user->login, utmp.login,
			sizeof(user->login));
		user->simul_count = 1;

		if (!rbtree_insert(inst->user_tree, user)) {
			rad_assert(0 == 1);
		}

		pthread_mutex_unlock(&cache->mutex);

	}

	/*
	 *	Entry was found, or newly created in the cache.
	 *	Seek to the place in the file.
	 */
	lseek(fd, nas_port->offset, SEEK_SET);

	/*
	 *	Lock the utmp file, prefer lockf() over flock().
	 */
	rad_lockfd(fd, LOCK_LEN);

	/*
	 *	If it WAS found in the cache, double-check it against
	 *	what is in the file.
	 */
	if (node) {
		/*
		 *	If we didn't read anything, then this entry
		 *	doesn't exist.
		 *
		 *	Similarly, if the entry in the file doesn't
		 *	match what we recall, then nuke the cache
		 *	entry.
		 */
		read_size = read(fd, &u, sizeof(u));
		if ((read_size < 0) ||
		    ((read_size > 0) && (read_size  != sizeof(u)))) {
			/*
			 *	Bad read, or bad record.
			 */
			radlog(L_ERR, "rlm_radutmp: Badly formed file %s",
			       cache->filename);
			close(fd);
			return RLM_MODULE_FAIL;
		}

		rad_assert(read_size != 0);

		/*
		 *	We've read a record, go poke at it.
		 */
		if (read_size > 0) {
			/*
			 *	If these aren't true, then
			 *
			 *	a) we have cached a "logout" entry,
			 *	   which we don't do.
			 *
			 *	b) we have cached the wrong NAS address
			 *
			 *	c) we have cached the wrong NAS port.
			 */
			rad_assert(u.type == P_LOGIN);
			rad_assert(u.nas_address == utmp.nas_address);
			rad_assert(u.nas_port == utmp.nas_port);

			/*
			 *	An update for the same session.
			 */
			if (strncmp(utmp.session_id, u.session_id,
				    sizeof(u.session_id)) == 0) {

				/*
				 *	It's a duplicate start, so we
				 *	don't bother writing it.
				 */
				if (status == PW_STATUS_START) {
					DEBUG2("  rlm_radutmp: Login entry for NAS %s port %u duplicate, ignoring it.",
					       nas, u.nas_port);
					close(fd);
					return RLM_MODULE_OK;


				/*
				 *	ALIVE for this session, keep the
				 *	original login time.
				 */
				} else if (status == PW_STATUS_ALIVE) {
					utmp.time = u.time;

				/*
				 *	Stop: delete it from our cache.
				 */
				} else if (status == PW_STATUS_STOP) {
					radutmp_simul_t *user, myUser;

					pthread_mutex_lock(&cache->mutex);
					rbtree_deletebydata(cache->nas_ports,
							    nas_port);

					strlcpy(myUser.login,
						u.login, sizeof(myUser.login));
					user = rbtree_finddata(inst->user_tree,
							       &myUser);
					rad_assert(user != NULL);
					rad_assert(user->simul_count > 0);

					user->simul_count--;
					if (user->simul_count == 0) {
						rbtree_deletebydata(inst->user_tree, user);
					}

					pthread_mutex_unlock(&cache->mutex);

 				} else {
					/*
					 *	We don't know how to
					 *	handle this.
					 */
					rad_assert(0 == 1);
				}

			} else { /* session ID doesn't match */
				/*
				 *	STOP for the right NAS & port,
				 *	but the Acct-Session-Id is
				 *	different.  This means that
				 *	we missed the original "stop",
				 *	and a new "start".
				 */
				if (status == PW_STATUS_STOP) {
					radlog(L_ERR, "rlm_radutmp: Logout entry for NAS %s port %u has old Acct-Session-ID, ignoring it.",
					       nas, u.nas_port);
					close(fd);
					return RLM_MODULE_OK;
				}
			} /* checked session ID's */
		}  /* else we haven't read anything from the file. */
	} /* else the entry wasn't cached, but could have been inserted */

	/*
	 *	Hmm... we may have received a start or alive packet
	 *	AFTER a stop or nas-down, in that case, we want to
	 *	discard the new packet.  However, the original code
	 *	could over-write an idle record with a new login
	 *	record for another NAS && port, so we won't worry
	 *	about this case too much.
	 */

	/*
	 *	Seek to where the entry is, and write it blindly.
	 */
	lseek(fd, nas_port->offset, SEEK_SET); /* FIXME: err */

	if (status != PW_STATUS_STOP) {
		utmp.type = P_LOGIN;
		rad_assert(nas_port != NULL); /* it WAS cached */
	} else {
		/* FIXME: maybe assert that the entry was deleted... */
		memcpy(&utmp, &u, sizeof(utmp));
		utmp.type = P_IDLE;
	}

	write(fd, &utmp, sizeof(utmp)); /* FIXME: err */

	close(fd);	/* and implicitly release the locks */

	return RLM_MODULE_OK;
}
Exemplo n.º 15
0
static void rs_packet_process(uint64_t count, rs_event_t *event, struct pcap_pkthdr const *header, uint8_t const *data)
{
	rs_stats_t		*stats = event->stats;
	struct timeval		elapsed;
	struct timeval		latency;

	/*
	 *	Pointers into the packet data we just received
	 */
	size_t len;
	uint8_t const		*p = data;
	struct ip_header const	*ip = NULL;	/* The IP header */
	struct ip_header6 const	*ip6 = NULL;	/* The IPv6 header */
	struct udp_header const	*udp;		/* The UDP header */
	uint8_t			version;	/* IP header version */
	bool			response;	/* Was it a response code */

	decode_fail_t		reason;		/* Why we failed decoding the packet */
	static uint64_t		captured = 0;

	RADIUS_PACKET *current;			/* Current packet were processing */
	rs_request_t *original;

	if (!start_pcap.tv_sec) {
		start_pcap = header->ts;
	}

	if (header->caplen <= 5) {
		INFO("Packet too small, captured %i bytes", header->caplen);
		return;
	}

	/*
	 *	Loopback header
	 */
	if ((p[0] == 2) && (p[1] == 0) && (p[2] == 0) && (p[3] == 0)) {
		p += 4;
	/*
	 *	Ethernet header
	 */
	} else {
		p += sizeof(struct ethernet_header);
	}

	version = (p[0] & 0xf0) >> 4;
	switch (version) {
	case 4:
		ip = (struct ip_header const *)p;
		len = (0x0f & ip->ip_vhl) * 4;	/* ip_hl specifies length in 32bit words */
		p += len;
		break;

	case 6:
		ip6 = (struct ip_header6 const *)p;
		p += sizeof(struct ip_header6);

		break;

	default:
		DEBUG("IP version invalid %i", version);
		return;
	}

	/*
	 *	End of variable length bits, do basic check now to see if packet looks long enough
	 */
	len = (p - data) + sizeof(struct udp_header) + (sizeof(radius_packet_t) - 1);	/* length value */
	if (len > header->caplen) {
		DEBUG("Packet too small, we require at least %zu bytes, captured %i bytes",
		      (size_t) len, header->caplen);
		return;
	}

	udp = (struct udp_header const *)p;
	p += sizeof(struct udp_header);

	/*
	 *	With artificial talloc memory limits there's a good chance we can
	 *	recover once some requests timeout, so make an effort to deal
	 *	with allocation failures gracefully.
	 */
	current = rad_alloc(conf, 0);
	if (!current) {
		ERROR("Failed allocating memory to hold decoded packet");
		rs_tv_add_ms(&header->ts, conf->stats.timeout, &stats->quiet);
		return;
	}
	current->timestamp = header->ts;
	current->data_len = header->caplen - (p - data);
	memcpy(&current->data, &p, sizeof(current->data));

	/*
	 *	Populate IP/UDP fields from PCAP data
	 */
	if (ip) {
		current->src_ipaddr.af = AF_INET;
		current->src_ipaddr.ipaddr.ip4addr.s_addr = ip->ip_src.s_addr;

		current->dst_ipaddr.af = AF_INET;
		current->dst_ipaddr.ipaddr.ip4addr.s_addr = ip->ip_dst.s_addr;
	} else {
		current->src_ipaddr.af = AF_INET6;
		memcpy(&current->src_ipaddr.ipaddr.ip6addr.s6_addr, &ip6->ip_src.s6_addr,
		       sizeof(current->src_ipaddr.ipaddr.ip6addr.s6_addr));

		current->dst_ipaddr.af = AF_INET6;
		memcpy(&current->dst_ipaddr.ipaddr.ip6addr.s6_addr, &ip6->ip_dst.s6_addr,
		       sizeof(current->dst_ipaddr.ipaddr.ip6addr.s6_addr));
	}

	current->src_port = ntohs(udp->udp_sport);
	current->dst_port = ntohs(udp->udp_dport);

	if (!rad_packet_ok(current, 0, &reason)) {
		RIDEBUG("(%" PRIu64 ") ** %s **", count, fr_strerror());
		RIDEBUG("(%" PRIu64 ") %s Id %i %s:%s:%d -> %s:%d\t+%u.%03u", count,
			fr_packet_codes[current->code], current->id,
			event->in->name,
			fr_inet_ntop(current->src_ipaddr.af, &current->src_ipaddr.ipaddr), current->src_port,
			fr_inet_ntop(current->dst_ipaddr.af, &current->dst_ipaddr.ipaddr), current->dst_port,
			(unsigned int) elapsed.tv_sec, ((unsigned int) elapsed.tv_usec / 1000));

		rad_free(&current);
		return;
	}

	switch (current->code) {
	case PW_CODE_ACCOUNTING_RESPONSE:
	case PW_CODE_AUTHENTICATION_REJECT:
	case PW_CODE_AUTHENTICATION_ACK:
	case PW_CODE_COA_NAK:
	case PW_CODE_COA_ACK:
	case PW_CODE_DISCONNECT_NAK:
	case PW_CODE_DISCONNECT_ACK:
	case PW_CODE_STATUS_CLIENT:
		{
			rs_request_t search;
			struct timeval when;

			rs_tv_add_ms(&header->ts, conf->stats.timeout, &when);

			/* look for a matching request and use it for decoding */
			search.packet = current;
			original = rbtree_finddata(request_tree, &search);

			/*
			 *	Only decode attributes if we want to print them or filter on them
			 *	rad_packet_ok does checks to verify the packet is actually valid.
			 */
			if (filter_vps || conf->print_packet) {
				if (rad_decode(current, original ? original->packet : NULL,
					       conf->radius_secret) != 0) {
					rad_free(&current);
					fr_perror("decode");
					return;
				}
			}

			/*
			 *	Check if we've managed to link it to a request
			 */
			if (original) {
				/*
				 *	Is this a retransmit?
				 */
				if (!original->linked) {
					original->stats_rsp = &stats->exchange[current->code];
				} else {
					RDEBUG("(%" PRIu64 ") ** RETRANSMISSION **", count);
					original->rt_rsp++;

					rad_free(&original->linked);
					fr_event_delete(event->list, &original->event);
				}

				original->linked = talloc_steal(original, current);

				/*
				 *	Some RADIUS servers and proxy servers may not cache
				 *	Accounting-Responses (and possibly other code),
				 *	and may immediately re-use a RADIUS packet src
				 *	port/id combination on receipt of a response.
				 */
				if (conf->dequeue[current->code]) {
					fr_event_delete(event->list, &original->event);
					rbtree_deletebydata(request_tree, original);
				} else {
					if (!fr_event_insert(event->list, rs_packet_cleanup, original, &when,
						    	     &original->event)) {
						ERROR("Failed inserting new event");
						/*
						 *	Delete the original request/event, it's no longer valid
						 *	for statistics.
						 */
						original->forced_cleanup = true;
						fr_event_delete(event->list, &original->event);
						rbtree_deletebydata(request_tree, original);

						return;
					}
				}
			/*
			 *	No request seen, or request was dropped by attribute filter
			 */
			} else {
				/*
				 *	If filter_vps are set assume the original request was dropped,
				 *	the alternative is maintaining another 'filter', but that adds
				 *	complexity, reduces max capture rate, and is generally a PITA.
				 */
				if (filter_vps) {
					rad_free(&current);
					RDEBUG2("(%" PRIu64 ") Dropped by attribute filter", count);
					return;
				}

				RDEBUG("(%" PRIu64 ") ** UNLINKED **", count);
				stats->exchange[current->code].interval.unlinked_total++;
			}

			response = true;
		}
			break;
	case PW_CODE_ACCOUNTING_REQUEST:
	case PW_CODE_AUTHENTICATION_REQUEST:
	case PW_CODE_COA_REQUEST:
	case PW_CODE_DISCONNECT_REQUEST:
	case PW_CODE_STATUS_SERVER:
		{
			rs_request_t search;
			struct timeval when;

			/*
			 *	Only decode attributes if we want to print them or filter on them
			 *	rad_packet_ok does checks to verify the packet is actually valid.
			 */
			if (filter_vps || conf->print_packet) {
				if (rad_decode(current, NULL, conf->radius_secret) != 0) {
					rad_free(&current);
					fr_perror("decode");
					return;
				}
			}

			/*
			 *	Now verify the packet passes the attribute filter
			 */
			if (filter_vps && !pairvalidate_relaxed(filter_vps, current->vps)) {
				rad_free(&current);
				RDEBUG2("(%" PRIu64 ") Dropped by attribute filter", count);
				return;
			}

			/*
			 *	save the request for later matching
			 */
			search.packet = rad_alloc_reply(conf, current);
			if (!search.packet) {
				ERROR("Failed allocating memory to hold expected reply");
				rs_tv_add_ms(&header->ts, conf->stats.timeout, &stats->quiet);
				rad_free(&current);
				return;
			}
			search.packet->code = current->code;

			rs_tv_add_ms(&header->ts, conf->stats.timeout, &when);

			original = rbtree_finddata(request_tree, &search);

			/*
			 *	Upstream device re-used src/dst ip/port id without waiting
			 *	for the timeout period to expire, or a response.
			 */
			if (original && memcmp(original->packet->vector, current->vector,
					       sizeof(original->packet->vector) != 0)) {
				RDEBUG2("(%" PRIu64 ") ** PREMATURE ID RE-USE **", count);
				stats->exchange[current->code].interval.reused_total++;
				original->forced_cleanup = true;

				fr_event_delete(event->list, &original->event);
				rbtree_deletebydata(request_tree, original);
				original = NULL;
			}

			if (original) {
				RDEBUG("(%" PRIu64 ") ** RETRANSMISSION **", count);
				original->rt_req++;

				rad_free(&original->packet);
				original->packet = talloc_steal(original, search.packet);

				/* We may of seen the response, but it may of been lost upstream */
				rad_free(&original->linked);
				fr_event_delete(event->list, &original->event);
			} else {
				original = talloc_zero(conf, rs_request_t);
				talloc_set_destructor(original, _request_free);

				original->id = count;
				original->in = event->in;
				original->stats_req = &stats->exchange[current->code];
				original->packet = talloc_steal(original, search.packet);

				rbtree_insert(request_tree, original);
			}

			/* update the timestamp in either case */
			original->packet->timestamp = header->ts;

			if (!fr_event_insert(event->list, rs_packet_cleanup, original, &when, &original->event)) {
				ERROR("Failed inserting new event");
				rbtree_deletebydata(request_tree, original);

				return;
			}
			response = false;
		}
			break;
		default:
			RDEBUG("** Unsupported code %i **", current->code);
			rad_free(&current);

			return;
	}

	if (event->out) {
		pcap_dump((void *) (event->out->dumper), header, data);
	}

	rs_tv_sub(&header->ts, &start_pcap, &elapsed);

	/*
	 *	Increase received count
	 */
	stats->exchange[current->code].interval.received_total++;

	/*
	 *	It's a linked response
	 */
	if (original && original->linked) {
		rs_tv_sub(&current->timestamp, &original->packet->timestamp, &latency);

		/*
		 *	Update stats for both the request and response types.
		 *
		 *	This isn't useful for things like Access-Requests, but will be useful for
		 *	CoA and Disconnect Messages, as we get the average latency across both
		 *	response types.
		 *
		 *	It also justifies allocating 255 instances rs_latency_t.
		 */
		rs_stats_update_latency(&stats->exchange[current->code], &latency);
		rs_stats_update_latency(&stats->exchange[original->packet->code], &latency);


		/*
		 *	Print info about the request/response.
		 */
		RIDEBUG("(%" PRIu64 ") %s Id %i %s:%s:%d %s %s:%d\t+%u.%03u\t+%u.%03u", count,
			fr_packet_codes[current->code], current->id,
			event->in->name,
			fr_inet_ntop(current->src_ipaddr.af, &current->src_ipaddr.ipaddr), current->src_port,
			response ? "<-" : "->",
			fr_inet_ntop(current->dst_ipaddr.af, &current->dst_ipaddr.ipaddr), current->dst_port,
			(unsigned int) elapsed.tv_sec, ((unsigned int) elapsed.tv_usec / 1000),
			(unsigned int) latency.tv_sec, ((unsigned int) latency.tv_usec / 1000));
	/*
	 *	It's the original request
	 */
	} else {
		/*
		 *	Print info about the request
		 */
		RIDEBUG("(%" PRIu64 ") %s Id %i %s:%s:%d %s %s:%d\t+%u.%03u", count,
			fr_packet_codes[current->code], current->id,
			event->in->name,
			fr_inet_ntop(current->src_ipaddr.af, &current->src_ipaddr.ipaddr), current->src_port,
			response ? "<-" : "->",
			fr_inet_ntop(current->dst_ipaddr.af, &current->dst_ipaddr.ipaddr), current->dst_port,
			(unsigned int) elapsed.tv_sec, ((unsigned int) elapsed.tv_usec / 1000));
	}

	if (conf->print_packet && (fr_debug_flag > 1) && current->vps) {
		pairsort(&current->vps, true);
		vp_printlist(log_dst, current->vps);
		pairfree(&current->vps);
	}

	if (!conf->to_stdout && (fr_debug_flag > 4)) {
		rad_print_hex(current);
	}

	fflush(log_dst);

	/*
	 *	If it's a request, a duplicate of the packet will of already been stored.
	 *	If it's a unlinked response, we need to free it explicitly, as it will
	 *	not be done by the event queue.
	 */
	if (!response || !original) {
		rad_free(&current);
	}

	captured++;
	/*
	 *	We've hit our capture limit, break out of the event loop
	 */
	if ((conf->limit > 0) && (captured >= conf->limit)) {
		INFO("Captured %" PRIu64 " packets, exiting...", captured);
		fr_event_loop_exit(events, 1);
	}
}
Exemplo n.º 16
0
static void rs_packet_cleanup(void *ctx)
{
	rs_request_t *request = talloc_get_type_abort(ctx, rs_request_t);
	RADIUS_PACKET *packet = request->packet;

	assert(request->stats_req);
	assert(!request->rt_rsp || request->stats_rsp);
	assert(packet);

	/*
	 *	Don't pollute stats or print spurious messages as radsniff closes.
	 */
	if (cleanup) {
		goto skip;
	}

	/*
	 *	Were at packet cleanup time which is when the packet was received + timeout
	 *	and it's not been linked with a forwarded packet or a response.
	 *
	 *	We now count it as lost.
	 */
	if (!request->linked && !request->forced_cleanup) {
		request->stats_req->interval.lost_total++;

		RDEBUG("(%i) ** LOST **", request->id);
		RIDEBUG("(%i) %s Id %i %s:%s:%d -> %s:%d", request->id,
			fr_packet_codes[packet->code], packet->id,
			request->in->name,
			fr_inet_ntop(packet->dst_ipaddr.af, &packet->dst_ipaddr.ipaddr), packet->dst_port,
			fr_inet_ntop(packet->src_ipaddr.af, &packet->src_ipaddr.ipaddr), packet->src_port);
	}

	/*
	 *	Now the request is done, we can update the retransmission stats
	 */
	if (request->rt_req > RS_RETRANSMIT_MAX) {
		request->stats_req->interval.rt_total[RS_RETRANSMIT_MAX]++;
	} else {
		request->stats_req->interval.rt_total[request->rt_req]++;
	}

	if (request->rt_rsp) {
		if (request->rt_rsp > RS_RETRANSMIT_MAX) {
			request->stats_rsp->interval.rt_total[RS_RETRANSMIT_MAX]++;
		} else {
			request->stats_rsp->interval.rt_total[request->rt_rsp]++;
		}
	}

	skip:

	/*
	 *	If were attempting to cleanup the request, and it's no longer in the request_tree
	 *	something has gone very badly wrong.
	 */
	assert(rbtree_deletebydata(request_tree, request));

	if (fr_event_list_num_elements(events) == 0) {
		fr_event_loop_exit(events, 1);
	}
}