/** Set the event list for a new IO instance
 *
 * @param[in] li the listener
 * @param[in] el the event list
 * @param[in] nr context from the network side
 */
static void mod_event_list_set(fr_listen_t *li, fr_event_list_t *el, UNUSED void *nr)
{
	proto_detail_file_t const  *inst = talloc_get_type_abort_const(li->app_io_instance, proto_detail_file_t);
	proto_detail_file_thread_t *thread = talloc_get_type_abort(li->thread_instance, proto_detail_file_thread_t);
	struct timeval when;

	thread->el = el;

	if (inst->immediate) {
		work_init(thread);
		return;
	}

	/*
	 *	Delay for a bit, before reading the detail files.
	 *	This gives the server time to call
	 *	rad_suid_down_permanent(), and for /proc/PID to
	 *	therefore change permissions, so that libkqueue can
	 *	read it.
	 */
	gettimeofday(&when, NULL);
	when.tv_sec +=1;

	if (fr_event_timer_insert(thread, thread->el, &thread->ev,
				  &when, work_retry_timer, thread) < 0) {
		ERROR("Failed inserting poll timer for %s", thread->filename_work);
	}
}
static int mod_close(fr_listen_t *li)
{
	proto_detail_file_t const  *inst = talloc_get_type_abort_const(li->app_io_instance, proto_detail_file_t);
	proto_detail_file_thread_t *thread = talloc_get_type_abort(li->thread_instance, proto_detail_file_thread_t);

	if (thread->nr) (void) fr_network_socket_delete(thread->nr, inst->parent->listen);

	/*
	 *	@todo - have our OWN event loop for timers, and a
	 *	"copy timer from -> to, which means we only have to
	 *	delete our child event loop from the parent on close.
	 */
	close(thread->fd);

	if (thread->vnode_fd >= 0) {
		if (thread->nr) {
			(void) fr_network_socket_delete(thread->nr, inst->parent->listen);
		} else {
			if (fr_event_fd_delete(thread->el, thread->vnode_fd, FR_EVENT_FILTER_VNODE) < 0) {
				PERROR("Failed removing DELETE callback on detach");
			}
		}
		close(thread->vnode_fd);
		thread->vnode_fd = -1;

		pthread_mutex_destroy(&thread->worker_mutex);
	}

	return 0;
}
/** Open a detail listener
 *
 */
static int mod_open(fr_listen_t *li)
{
	proto_detail_file_t const  *inst = talloc_get_type_abort_const(li->app_io_instance, proto_detail_file_t);
	proto_detail_file_thread_t *thread = talloc_get_type_abort(li->thread_instance, proto_detail_file_thread_t);

	if (inst->poll_interval == 0) {
		int oflag;

#ifdef O_EVTONLY
		oflag = O_EVTONLY;
#else
		oflag = O_RDONLY;
#endif
		li->fd = thread->fd = open(inst->directory, oflag);
	} else {
		li->fd = thread->fd = open("/dev/null", O_RDONLY);
	}
	if (thread->fd < 0) {
		cf_log_err(inst->cs, "Failed opening %s: %s", inst->directory, fr_syserror(errno));
		return -1;
	}

	thread->inst = inst;
	thread->name = talloc_typed_asprintf(thread, "detail polling for files matching %s", inst->filename);
	thread->vnode_fd = -1;
	pthread_mutex_init(&thread->worker_mutex, NULL);

	DEBUG("Listening on %s bound to virtual server %s",
	      thread->name, cf_section_name2(inst->parent->server_cs));

	return 0;
}
Esempio n. 4
0
/** Continue after unlang_resumable()
 *
 */
static rlm_rcode_t mod_radius_resume(REQUEST *request, void *instance, void *thread, void *ctx)
{
	rlm_radius_t const *inst = talloc_get_type_abort_const(instance, rlm_radius_t);
	rlm_radius_thread_t *t = talloc_get_type_abort(thread, rlm_radius_thread_t);

	return inst->io->resume(request, inst->io_instance, t->thread_io_ctx, ctx);
}
Esempio n. 5
0
static xlat_action_t redis_remap_xlat(TALLOC_CTX *ctx, fr_cursor_t *out,
				      REQUEST *request, void const *xlat_inst,
				      UNUSED void *xlat_thread_inst,
				      fr_value_box_t **in)
{
	rlm_redis_t const		*inst = talloc_get_type_abort_const(*((void const * const *)xlat_inst),
									    rlm_redis_t);

	fr_socket_addr_t		node_addr;
	fr_pool_t			*pool;
	fr_redis_conn_t			*conn;
	fr_redis_cluster_rcode_t	rcode;
	fr_value_box_t			*vb;

	if (!in) {
		REDEBUG("Missing key");
		return XLAT_ACTION_FAIL;
	}

	if (fr_value_box_list_concat(ctx, *in, in, FR_TYPE_STRING, true) < 0) {
		RPEDEBUG("Failed concatenating input");
		return XLAT_ACTION_FAIL;
	}

	if (fr_inet_pton_port(&node_addr.ipaddr, &node_addr.port, (*in)->vb_strvalue, (*in)->vb_length,
			      AF_UNSPEC, true, true) < 0) {
		RPEDEBUG("Failed parsing node address");
		return XLAT_ACTION_FAIL;
	}

	if (fr_redis_cluster_pool_by_node_addr(&pool, inst->cluster, &node_addr, true) < 0) {
		RPEDEBUG("Failed locating cluster node");
		return XLAT_ACTION_FAIL;
	}

	conn = fr_pool_connection_get(pool, request);
	if (!conn) {
		REDEBUG("No connections available for cluster node");
		return XLAT_ACTION_FAIL;
	}

	rcode = fr_redis_cluster_remap(request, inst->cluster, conn);
	fr_pool_connection_release(pool, request, conn);

	MEM(vb = fr_value_box_alloc_null(ctx));
	fr_value_box_strdup(vb, vb, NULL, fr_int2str(fr_redis_cluster_rcodes_table, rcode, "<INVALID>"), false);
	fr_cursor_append(out, vb);

	return XLAT_ACTION_DONE;
}
/** Set the file descriptor for this socket.
 *
 */
static int mod_fd_set(fr_listen_t *li, int fd)
{
	proto_dhcpv4_udp_t const	*inst = talloc_get_type_abort_const(li->app_io_instance, proto_dhcpv4_udp_t);
	proto_dhcpv4_udp_thread_t	*thread = talloc_get_type_abort(li->thread_instance, proto_dhcpv4_udp_thread_t);

	thread->sockfd = fd;

	thread->name = fr_app_io_socket_name(thread, &proto_dhcpv4_udp,
					     &thread->connection->src_ipaddr, thread->connection->src_port,
					     &inst->ipaddr, inst->port,
					     inst->interface);

	return 0;
}
Esempio n. 7
0
/** Signal the network to read from a listener
 *
 * @param nr the network
 * @param listen the listener to read from
 */
void fr_network_listen_read(fr_network_t *nr, fr_listen_t const *listen)
{
	fr_network_socket_t my_socket, *s;

	(void) talloc_get_type_abort(nr, fr_network_t);
	(void) talloc_get_type_abort_const(listen, fr_listen_t);

	my_socket.listen = listen;
	s = rbtree_finddata(nr->sockets, &my_socket);
	if (!s) return;

	/*
	 *	Go read the socket.
	 */
	fr_network_read(nr->el, s->fd, 0, s);
}
Esempio n. 8
0
/** Return the node that is currently servicing a particular key
 *
 *
 */
static xlat_action_t redis_node_xlat(TALLOC_CTX *ctx, fr_cursor_t *out,
				     REQUEST *request, void const *xlat_inst,
				     UNUSED void *xlat_thread_inst,
				     fr_value_box_t **in)
{
	rlm_redis_t const			*inst = talloc_get_type_abort_const(*((void const * const *)xlat_inst),
										    rlm_redis_t);

	fr_redis_cluster_key_slot_t const	*key_slot;
	fr_redis_cluster_node_t const		*node;
	fr_ipaddr_t				ipaddr;
	uint16_t				port;

	char const				*p;
	char					*q;
	char const				*key;
	size_t					key_len;
	unsigned long				idx = 0;
	fr_value_box_t				*vb;

	if (!in) {
		REDEBUG("Missing key");
		return XLAT_ACTION_FAIL;
	}

	if (fr_value_box_list_concat(ctx, *in, in, FR_TYPE_STRING, true) < 0) {
		RPEDEBUG("Failed concatenating input");
		return XLAT_ACTION_FAIL;
	}

	key = p = (*in)->vb_strvalue;
	p = strchr(p, ' ');		/* Look for index */
	if (p) {
		key_len = p - key;

		idx = strtoul(p, &q, 10);
		if (q == p) {
			REDEBUG("Tailing garbage after node index");
			return XLAT_ACTION_FAIL;
		}
	} else {
		key_len = (*in)->vb_length;
	}

	key_slot = fr_redis_cluster_slot_by_key(inst->cluster, request, (uint8_t const *)key, key_len);
	if (idx == 0) {
		node = fr_redis_cluster_master(inst->cluster, key_slot);
	} else {
		node = fr_redis_cluster_slave(inst->cluster, key_slot, idx - 1);
	}

	if (!node) {
		RDEBUG2("No node available for this key slot");
		return XLAT_ACTION_DONE;
	}

	if ((fr_redis_cluster_ipaddr(&ipaddr, node) < 0) || (fr_redis_cluster_port(&port, node) < 0)) {
		REDEBUG("Failed retrieving node information");
		return XLAT_ACTION_FAIL;
	}

	MEM(vb = fr_value_box_alloc_null(ctx));
	fr_value_box_asprintf(vb, vb, NULL, false, "%pV:%u", fr_box_ipaddr(ipaddr), port);
	fr_cursor_append(out, vb);

	return XLAT_ACTION_DONE;
}
static int test_fd(void const *ctx)
{
	fr_listen_test_t const *io_ctx = talloc_get_type_abort_const(ctx, fr_listen_test_t);

	return io_ctx->sockfd;
}
Esempio n. 10
0
/** Open a UDP listener for DHCPV4
 *
 */
static int mod_open(fr_listen_t *li)
{
	proto_dhcpv4_udp_t const	*inst = talloc_get_type_abort_const(li->app_io_instance, proto_dhcpv4_udp_t);
	proto_dhcpv4_udp_thread_t	*thread = talloc_get_type_abort(li->thread_instance, proto_dhcpv4_udp_thread_t);

	int				sockfd, rcode;
	uint16_t			port = inst->port;
	CONF_SECTION			*server_cs;
	CONF_ITEM			*ci;

	li->fd = sockfd = fr_socket_server_udp(&inst->ipaddr, &port, inst->port_name, true);
	if (sockfd < 0) {
		PERROR("Failed opening UDP socket");
	error:
		return -1;
	}

	/*
	 *	Set SO_REUSEPORT before bind, so that all packets can
	 *	listen on the same destination IP address.
	 */
	if (1) {
		int on = 1;

		if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on)) < 0) {
			ERROR("Failed to set socket 'reuseport': %s", fr_syserror(errno));
			close(sockfd);
			return -1;
		}
	}

	if (inst->broadcast) {
		int on = 1;

		if (setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)) < 0) {
			ERROR("Failed to set broadcast option: %s", fr_syserror(errno));
			close(sockfd);
			return -1;
		}
	}

	/*
	 *	SUID up is really only needed if interface is set, OR port <1024.
	 */
	rad_suid_up();
	rcode = fr_socket_bind(sockfd, &inst->ipaddr, &port, inst->interface);
	rad_suid_down();
	if (rcode < 0) {
		close(sockfd);
		PERROR("Failed binding socket");
		goto error;
	}

	thread->sockfd = sockfd;

	ci = cf_parent(inst->cs); /* listen { ... } */
	rad_assert(ci != NULL);
	ci = cf_parent(ci);
	rad_assert(ci != NULL);

	server_cs = cf_item_to_section(ci);

	thread->name = fr_app_io_socket_name(thread, &proto_dhcpv4_udp,
					     NULL, 0,
					     &inst->ipaddr, inst->port,
					     inst->interface);

	DEBUG("Listening on dhcpv4 address %s bound to virtual server %s",
	      thread->name, cf_section_name2(server_cs));

	return 0;
}
Esempio n. 11
0
static ssize_t mod_write(fr_listen_t *li, void *packet_ctx, UNUSED fr_time_t request_time,
			 uint8_t *buffer, size_t buffer_len, UNUSED size_t written)
{
	proto_dhcpv4_udp_t const	*inst = talloc_get_type_abort_const(li->app_io_instance, proto_dhcpv4_udp_t);
	proto_dhcpv4_udp_thread_t	*thread = talloc_get_type_abort(li->thread_instance, proto_dhcpv4_udp_thread_t);

	fr_io_track_t			*track = talloc_get_type_abort(packet_ctx, fr_io_track_t);
	fr_io_address_t			address;

	int				flags;
	ssize_t				data_size;

	/*
	 *	@todo - share a stats interface with the parent?  or
	 *	put the stats in the listener, so that proto_dhcpv4
	 *	can update them, too.. <sigh>
	 */
	thread->stats.total_responses++;

	flags = UDP_FLAGS_CONNECTED * (thread->connection != NULL);

	rad_assert(track->reply_len == 0);

	/*
	 *	Swap src/dst IP/port
	 */
	address.src_ipaddr = track->address->dst_ipaddr;
	address.src_port = track->address->dst_port;
	address.dst_ipaddr = track->address->src_ipaddr;
	address.dst_port = track->address->src_port;
	address.if_index = track->address->if_index;

	/*
	 *	Figure out which kind of packet we're sending.
	 */
	if (!thread->connection) {
		uint8_t const *code, *sid;
		uint32_t ipaddr;
		dhcp_packet_t *packet = (dhcp_packet_t *) buffer;
		dhcp_packet_t *request = (dhcp_packet_t *) track->packet; /* only 20 bytes tho! */
#ifdef WITH_IFINDEX_IPADDR_RESOLUTION
		fr_ipaddr_t primary;
#endif

		/*
		 *	This isn't available in the packet header.
		 */
		code = fr_dhcpv4_packet_get_option(packet, buffer_len, attr_message_type);
		if (!code || (code[1] < 1) || (code[2] == 0) || (code[2] >= FR_DHCP_INFORM)) {
			DEBUG("WARNING - silently discarding reply due to invalid or missing message type");
			return 0;
		}

		/*
		 *	Set the source IP of the packet.
		 *
		 *	- if src_ipaddr is unicast, use that
		 *	- else if socket wasn't bound to *, then use that
		 *	- else if we have if_index, get main IP from that interface and use that.
		 *	- else for offer/ack, look at option 54, for Server Identification and use that
		 *	- else leave source IP as whatever is already in "address.src_ipaddr".
		 */
		if (inst->src_ipaddr.addr.v4.s_addr != INADDR_ANY) {
			address.src_ipaddr = inst->src_ipaddr;

		} else if (inst->ipaddr.addr.v4.s_addr != INADDR_ANY) {
			address.src_ipaddr = inst->ipaddr;

#ifdef WITH_IFINDEX_IPADDR_RESOLUTION
		} else if ((address->if_index > 0) &&
			   (fr_ipaddr_from_ifindex(&primary, thread->sockfd, &address.dst_ipaddr.af,
						   &address.if_index) == 0)) {
			address.src_ipaddr = primary;
#endif
		} else if (((code[2] == FR_DHCP_OFFER) || (code[2] == FR_DHCP_ACK)) &&
			   ((sid = fr_dhcpv4_packet_get_option(packet, buffer_len, attr_dhcp_server_identifier)) != NULL) &&
			   (sid[1] == 4)) {
			memcpy(&address.src_ipaddr.addr.v4.s_addr, sid + 2, 4);
		}

		/*
		 *	We have GIADDR in the packet, so send it
		 *	there.  The packet is FROM our IP address and
		 *	port, TO the destination IP address, at the
		 *	same (i.e. server) port.
		 */
		memcpy(&ipaddr, &packet->giaddr, 4);
		if (ipaddr != INADDR_ANY) {
			DEBUG("Reply will be sent to giaddr.");
			address.dst_ipaddr.addr.v4.s_addr = ipaddr;
			address.dst_port = inst->port;
			address.src_port = inst->port;

			/*
			 *	Increase the hop count for client
			 *	packets sent to the next gateway.
			 */
			if ((code[2] == FR_DHCP_DISCOVER) ||
			    (code[2] == FR_DHCP_REQUEST)) {
				packet->opcode = 1; /* client message */
				packet->hops = request->hops + 1;
			} else {
				packet->opcode = 2; /* server message */
			}

			goto send_reply;
		}

		/*
		 *	If there's no GIADDR, we don't know where to
		 *	send client packets.
		 */
		if ((code[2] == FR_DHCP_DISCOVER) || (code[2] == FR_DHCP_REQUEST)) {
			DEBUG("WARNING - silently discarding client reply, as there is no GIADDR to send it to.");
			return 0;
		}

		packet->opcode = 2; /* server message */

		/*
		 *	The original packet requested a broadcast
		 *	reply, and CIADDR is empty, go broadcast the
		 *	reply.  RFC 2131 page 23.
		 */
		if (((request->flags & FR_DHCP_FLAGS_VALUE_BROADCAST) != 0) &&
		    (request->ciaddr == INADDR_ANY)) {
			DEBUG("Reply will be broadcast due to client request.");
			address.dst_ipaddr.addr.v4.s_addr = INADDR_BROADCAST;
			goto send_reply;
		}

		/*
		 *	The original packet has CIADDR, so we unicast
		 *	the reply there.  RFC 2131 page 23.
		 */
		if (request->ciaddr != INADDR_ANY) {
			DEBUG("Reply will be unicast to CIADDR from original packet.");
			memcpy(&address.dst_ipaddr.addr.v4.s_addr, &request->ciaddr, 4);
			goto send_reply;
		}

		/*
		 *	The original packet was unicast to us, such as
		 *	via a relay.  We have a unicast destination
		 *	address, so we just use that.
		 */
		if ((packet->yiaddr == htonl(INADDR_ANY)) &&
		    (address.dst_ipaddr.addr.v4.s_addr != htonl(INADDR_BROADCAST))) {
			DEBUG("Reply will be unicast to source IP from original packet.");
			goto send_reply;
		}

		switch (code[2]) {
			/*
			 *	Offers are sent to YIADDR if we
			 *	received a unicast packet from YIADDR.
			 *	Otherwise, they are unicast to YIADDR
			 *	(if we can update ARP), otherwise they
			 *	are broadcast.
			 */
		case FR_DHCP_OFFER:
			/*
			 *	If the packet was unicast from the
			 *	client, unicast it back.
			 */
			if (memcmp(&address.dst_ipaddr.addr.v4.s_addr, &packet->yiaddr, 4) == 0) {
				DEBUG("Reply will be unicast to YIADDR.");

#ifdef SIOCSARP
			} else if (inst->broadcast && inst->interface) {
				if (fr_dhcpv4_udp_add_arp_entry(thread->sockfd, inst->interface,
								&packet->yiaddr, &packet->chaddr) < 0) {
					DEBUG("Failed adding ARP entry.  Reply will be broadcast.");
					address.dst_ipaddr.addr.v4.s_addr = INADDR_BROADCAST;
				} else {
					DEBUG("Reply will be unicast to YIADDR after ARP table updates.");
				}

#endif
			} else {
				DEBUG("Reply will be broadcast due to OFFER.");
				address.dst_ipaddr.addr.v4.s_addr = INADDR_BROADCAST;
			}
			break;

			/*
			 *	ACKs are unicast to YIADDR
			 */
		case FR_DHCP_ACK:
			DEBUG("Reply will be unicast to YIADDR.");
			memcpy(&address.dst_ipaddr.addr.v4.s_addr, &packet->yiaddr, 4);
			break;

			/*
			 *	NAKs are broadcast.
			 */
		case FR_DHCP_NAK:
			DEBUG("Reply will be broadcast due to NAK.");
			address.dst_ipaddr.addr.v4.s_addr = INADDR_BROADCAST;
			break;

		default:
			DEBUG("WARNING - silently discarding reply due to invalid message type %d", code[2]);
			return 0;
		}
	}

send_reply:
	/*
	 *	proto_dhcpv4 takes care of suppressing do-not-respond, etc.
	 */
	data_size = udp_send(thread->sockfd, buffer, buffer_len, flags,
			     &address.src_ipaddr, address.src_port,
			     address.if_index,
			     &address.dst_ipaddr, address.dst_port);

	/*
	 *	This socket is dead.  That's an error...
	 */
	if (data_size <= 0) return data_size;

	return data_size;
}
/*
 *	All of the decoding is done by proto_detail and proto_detail_work
 */
static int mod_decode(void const *instance, REQUEST *request, uint8_t *const data, size_t data_len)
{
	proto_detail_file_t const     	*inst = talloc_get_type_abort_const(instance, proto_detail_file_t);

	return inst->parent->work_io->decode(inst->parent->work_io_instance, request, data, data_len);
}
Esempio n. 13
0
/** Allow the admin to set packet contents for Status-Server ping checks
 *
 * @param[in] ctx	to allocate data in (instance of proto_radius).
 * @param[out] out	Where to write our parsed data
 * @param[in] parent	Base structure address.
 * @param[in] ci	#CONF_SECTION specifying the things to update
 * @param[in] rule	unused.
 * @return
 *	- 0 on success.
 *	- -1 on failure.
 */
static int status_check_update_parse(TALLOC_CTX *ctx, void *out, UNUSED void *parent,
				     CONF_ITEM *ci, UNUSED CONF_PARSER const *rule)
{
	int			rcode;
	CONF_SECTION		*cs;
	char const		*name2;
	vp_map_t		*head = NULL;

	rad_assert(cf_item_is_section(ci));

	cs = cf_item_to_section(ci);
	name2 = cf_section_name2(cs);
	if (!name2 || (strcmp(name2, "request") != 0)) {
		cf_log_err(cs, "You must specify 'request' as the destination list");
		return -1;
	}

	/*
	 *	Compile the "update" section.
	 */
	{
		vp_tmpl_rules_t	parse_rules = {
			.allow_foreign = true	/* Because we don't know where we'll be called */
		};

		rcode = map_afrom_cs(ctx, &head, cs, &parse_rules, &parse_rules, unlang_fixup_update, NULL, 128);
		if (rcode < 0) return -1; /* message already printed */
		if (!head) {
			cf_log_err(cs, "'update' sections cannot be empty");
			return -1;
		}
	}

	/*
	 *	Rely on "bootstrap" to do sanity checks between 'type
	 *	= Access-Request', and 'update' containing passwords.
	 */
	memcpy(out, &head, sizeof(head));

	return 0;
}


static void mod_radius_signal(REQUEST *request, void *instance, void *thread, void *ctx,
			      fr_state_signal_t action)
{
	rlm_radius_t const *inst = talloc_get_type_abort_const(instance, rlm_radius_t);
	rlm_radius_thread_t *t = talloc_get_type_abort(thread, rlm_radius_thread_t);

	/*
	 *	We've been told we're done.  Clean up.
	 *
	 *	Note that the caller doesn't necessarily need to send
	 *	us the signal, as he can just talloc_free(request).
	 *	But it is more polite to send a signal, and it allows
	 *	the IO modules to do additional debugging if
	 *	necessary.
	 */
	if (action == FR_SIGNAL_CANCEL) {
		talloc_free(ctx);
		return;
	}

	/*
	 *	We received a duplicate packet, but we're not doing
	 *	synchronous proxying.  Ignore the dup, and rely on the
	 *	IO submodule to time it's own retransmissions.
	 */
	if ((action == FR_SIGNAL_DUP) && !inst->synchronous) return;

	if (!inst->io->signal) return;

	inst->io->signal(request, inst->io_instance, t->thread_io_ctx, ctx, action);
}
Esempio n. 14
0
/** Open a UDP listener for RADIUS
 *
 */
static int mod_open(fr_listen_t *li)
{
	proto_radius_udp_t const       	*inst = talloc_get_type_abort_const(li->app_io_instance, proto_radius_udp_t);
	proto_radius_udp_thread_t	*thread = talloc_get_type_abort(li->thread_instance, proto_radius_udp_thread_t);

	int				sockfd;
	uint16_t			port = inst->port;
	CONF_SECTION			*server_cs;
	CONF_ITEM			*ci;

	li->fd = sockfd = fr_socket_server_udp(&inst->ipaddr, &port, inst->port_name, true);
	if (sockfd < 0) {
		PERROR("Failed opening UDP socket");
	error:
		return -1;
	}

	/*
	 *	Set SO_REUSEPORT before bind, so that all packets can
	 *	listen on the same destination IP address.
	 */
	if (1) {
		int on = 1;

		if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on)) < 0) {
			ERROR("Failed to set socket 'reuseport': %s", fr_syserror(errno));
			return -1;
		}
	}

#ifdef SO_RCVBUF
	if (inst->recv_buff_is_set) {
		int opt;

		opt = inst->recv_buff;
		if (setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(int)) < 0) {
			WARN("Failed setting 'recv_buf': %s", fr_syserror(errno));
		}
	}
#endif

#ifdef SO_SNDBUF
	if (inst->send_buff_is_set) {
		int opt;

		opt = inst->send_buff;
		if (setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, &opt, sizeof(int)) < 0) {
			WARN("Failed setting 'send_buf': %s", fr_syserror(errno));
		}
	}
#endif

	if (fr_socket_bind(sockfd, &inst->ipaddr, &port, inst->interface) < 0) {
		close(sockfd);
		PERROR("Failed binding socket");
		goto error;
	}

	thread->sockfd = sockfd;

	ci = cf_parent(inst->cs); /* listen { ... } */
	rad_assert(ci != NULL);
	ci = cf_parent(ci);
	rad_assert(ci != NULL);

	server_cs = cf_item_to_section(ci);

	thread->name = fr_app_io_socket_name(thread, &proto_radius_udp,
					     NULL, 0,
					     &inst->ipaddr, inst->port,
					     inst->interface);

	// @todo - also print out auth / acct / coa, etc.
	DEBUG("Listening on radius address %s bound to virtual server %s",
	      thread->name, cf_section_name2(server_cs));

	return 0;
}
Esempio n. 15
0
static ssize_t mod_read(fr_listen_t *li, void **packet_ctx, fr_time_t **recv_time, uint8_t *buffer, size_t buffer_len, size_t *leftover, UNUSED uint32_t *priority, UNUSED bool *is_dup)
{
	proto_radius_udp_t const       	*inst = talloc_get_type_abort_const(li->app_io_instance, proto_radius_udp_t);
	proto_radius_udp_thread_t	*thread = talloc_get_type_abort(li->thread_instance, proto_radius_udp_thread_t);
	fr_io_address_t			*address, **address_p;

	int				flags;
	ssize_t				data_size;
	size_t				packet_len;
	struct timeval			timestamp;
	decode_fail_t			reason;

	fr_time_t			*recv_time_p;

	*leftover = 0;		/* always for UDP */

	/*
	 *	Where the addresses should go.  This is a special case
	 *	for proto_radius.
	 */
	address_p = (fr_io_address_t **) packet_ctx;
	address = *address_p;
	recv_time_p = *recv_time;

	/*
	 *      Tell udp_recv if we're connected or not.
	 */
	flags = UDP_FLAGS_CONNECTED * (thread->connection != NULL);

	data_size = udp_recv(thread->sockfd, buffer, buffer_len, flags,
			     &address->src_ipaddr, &address->src_port,
			     &address->dst_ipaddr, &address->dst_port,
			     &address->if_index, &timestamp);
	if (data_size < 0) {
		DEBUG2("proto_radius_udp got read error: %s", fr_strerror());
		return data_size;
	}

	if (!data_size) {
		DEBUG2("proto_radius_udp got no data: ignoring");
		return 0;
	}

	packet_len = data_size;

	if (data_size < 20) {
		DEBUG2("proto_radius_udp got 'too short' packet size %zd", data_size);
		thread->stats.total_malformed_requests++;
		return 0;
	}

	if (packet_len > inst->max_packet_size) {
		DEBUG2("proto_radius_udp got 'too long' packet size %zd > %u", data_size, inst->max_packet_size);
		thread->stats.total_malformed_requests++;
		return 0;
	}

	if ((buffer[0] == 0) || (buffer[0] > FR_MAX_PACKET_CODE)) {
		DEBUG("proto_radius_udp got invalid packet code %d", buffer[0]);
		thread->stats.total_unknown_types++;
		return 0;
	}

	/*
	 *      If it's not a RADIUS packet, ignore it.
	 */
	if (!fr_radius_ok(buffer, &packet_len, inst->max_attributes, false, &reason)) {
		/*
		 *      @todo - check for F5 load balancer packets.  <sigh>
		 */
		DEBUG2("proto_radius_udp got a packet which isn't RADIUS");
		thread->stats.total_malformed_requests++;
		return 0;
	}

	// @todo - maybe convert timestamp?
	*recv_time_p = fr_time();

	/*
	 *	proto_radius sets the priority
	 */

	/*
	 *	Print out what we received.
	 */
	DEBUG2("proto_radius_udp - Received %s ID %d length %d %s",
	       fr_packet_codes[buffer[0]], buffer[1],
	       (int) packet_len, thread->name);

	return packet_len;
}