/*
 * ecm_state_char_device_release()
 *	Called when a process closes the device file.
 */
static int ecm_state_char_device_release(struct inode *inode, struct file *file)
{
	struct ecm_state_file_instance *sfi;

	sfi = (struct ecm_state_file_instance *)file->private_data;
	DEBUG_CHECK_MAGIC(sfi, ECM_STATE_FILE_INSTANCE_MAGIC, "%p: magic failed", sfi);
	DEBUG_INFO("%p: State close\n", sfi);

	/*
	 * Release any references held
	 */
	if (sfi->ci) {
		ecm_db_connection_deref(sfi->ci);
	}
	if (sfi->mi) {
		ecm_db_mapping_deref(sfi->mi);
	}
	if (sfi->hi) {
		ecm_db_host_deref(sfi->hi);
	}
	if (sfi->ni) {
		ecm_db_node_deref(sfi->ni);
	}
	if (sfi->ii) {
		ecm_db_iface_deref(sfi->ii);
	}
#ifdef ECM_DB_CTA_TRACK_ENABLE
	ecm_state_file_classifier_type_assignments_release(sfi);
#endif

	DEBUG_CLEAR_MAGIC(sfi);
	kfree(sfi);

	return 0;
}
Example #2
0
/*
 * ecm_tracker_tcp_deref_callback()
 */
int ecm_tracker_tcp_deref_callback(struct ecm_tracker_instance *ti)
{
	struct ecm_tracker_tcp_internal_instance *ttii = (struct ecm_tracker_tcp_internal_instance *)ti;
	int refs;
	DEBUG_CHECK_MAGIC(ttii, ECM_TRACKER_TCP_INSTANCE_MAGIC, "%p: magic failed", ttii);

	spin_lock_bh(&ttii->lock);
	ttii->refs--;
	refs = ttii->refs;
	DEBUG_ASSERT(ttii->refs >= 0, "%p: ref wrap", ttii);
	DEBUG_TRACE("%p: deref %d\n", ttii, ttii->refs);

	if (ttii->refs > 0) {
		spin_unlock_bh(&ttii->lock);
		return refs;
	}

	DEBUG_TRACE("%p: final\n", ttii);
	spin_unlock_bh(&ttii->lock);

	spin_lock_bh(&ecm_tracker_tcp_lock);
	ecm_tracker_tcp_count--;
	DEBUG_ASSERT(ecm_tracker_tcp_count >= 0, "%p: tracker count wrap", ttii);
	spin_unlock_bh(&ecm_tracker_tcp_lock);

	DEBUG_INFO("%p: TCP tracker final\n", ttii);
	DEBUG_CLEAR_MAGIC(ttii);
	kfree(ttii);

	return 0;
}
/*
 * ecm_state_prefix_remove()
 *	Remove level from the prefix
 *
 * Returns 0 on success
 */
int ecm_state_prefix_remove(struct ecm_state_file_instance *sfi)
{
	int pxsz;

	DEBUG_CHECK_MAGIC(sfi, ECM_STATE_FILE_INSTANCE_MAGIC, "%p: magic failed", sfi);

	sfi->prefix_level--;
	DEBUG_ASSERT(sfi->prefix_level >= 0, "Bad prefix handling\n");
	pxsz = sfi->prefix_levels[sfi->prefix_level];
	sfi->prefix[pxsz] = 0;
	return 0;
}
Example #4
0
/*
 * ecm_tracker_tcp_state_get_callback()
 * 	Get state
 */
static void ecm_tracker_tcp_state_get_callback(struct ecm_tracker_instance *ti, ecm_tracker_sender_state_t *src_state,
					ecm_tracker_sender_state_t *dest_state, ecm_tracker_connection_state_t *state, ecm_db_timer_group_t *tg)
{
	struct ecm_tracker_tcp_internal_instance *ttii = (struct ecm_tracker_tcp_internal_instance *)ti;
	DEBUG_CHECK_MAGIC(ttii, ECM_TRACKER_TCP_INSTANCE_MAGIC, "%p: magic failed", ttii);
	spin_lock_bh(&ttii->lock);
	*src_state = ttii->sender_states[ECM_TRACKER_SENDER_TYPE_SRC].state;
	*dest_state = ttii->sender_states[ECM_TRACKER_SENDER_TYPE_DEST].state;
	spin_unlock_bh(&ttii->lock);
	*state = ecm_tracker_tcp_connection_state_matrix[*src_state][*dest_state];
	*tg = ecm_tracker_tcp_timer_group_from_state[*state];
}
Example #5
0
/*
 * ecm_tracker_tcp_ref_callback()
 */
void ecm_tracker_tcp_ref_callback(struct ecm_tracker_instance *ti)
{
	struct ecm_tracker_tcp_internal_instance *ttii = (struct ecm_tracker_tcp_internal_instance *)ti;
	DEBUG_CHECK_MAGIC(ttii, ECM_TRACKER_TCP_INSTANCE_MAGIC, "%p: magic failed", ttii);

	spin_lock_bh(&ttii->lock);

	ttii->refs++;
	DEBUG_ASSERT(ttii->refs > 0, "%p: ref wrap", ttii);
	DEBUG_TRACE("%p: ref %d\n", ttii, ttii->refs);

	spin_unlock_bh(&ttii->lock);
}
/*
 * ecm_state_write_reset()
 *	Reset the msg buffer, specifying a new initial prefix
 *
 * Returns 0 on success
 */
int ecm_state_write_reset(struct ecm_state_file_instance *sfi, char *prefix)
{
	int result;

	DEBUG_CHECK_MAGIC(sfi, ECM_STATE_FILE_INSTANCE_MAGIC, "%p: magic failed", sfi);
	sfi->msgp = sfi->msg;
	sfi->msg_len = 0;

	result = snprintf(sfi->prefix, ECM_STATE_FILE_PREFIX_SIZE, "%s", prefix);
	if ((result < 0) || (result >= ECM_STATE_FILE_PREFIX_SIZE)) {
		return -1;
	}
	sfi->prefix_level = 0;
	sfi->prefix_levels[sfi->prefix_level] = result;
	return 0;
}
/*
 * ecm_state_prefix_index_add()
 *	Add another level (numeric) to the prefix
 *
 * Returns 0 on success
 */
int ecm_state_prefix_index_add(struct ecm_state_file_instance *sfi, int index)
{
	int pxsz;
	int pxremain;
	int result;

	DEBUG_CHECK_MAGIC(sfi, ECM_STATE_FILE_INSTANCE_MAGIC, "%p: magic failed", sfi);

	pxsz = sfi->prefix_levels[sfi->prefix_level];
	pxremain = ECM_STATE_FILE_PREFIX_SIZE - pxsz;
	result = snprintf(sfi->prefix + pxsz, pxremain, ".%d", index);
	if ((result < 0) || (result >= pxremain)) {
		return -1;
	}

	sfi->prefix_level++;
	DEBUG_ASSERT(sfi->prefix_level < ECM_STATE_FILE_PREFIX_LEVELS_MAX, "Bad prefix handling\n");
	sfi->prefix_levels[sfi->prefix_level] = pxsz + result;
	return 0;
}
/*
 * ecm_state_write()
 *	Write out to the message buffer, prefix is added automatically.
 *
 * Returns 0 on success
 */
int ecm_state_write(struct ecm_state_file_instance *sfi, char *name, char *fmt, ...)
{
	int remain;
	char *ptr;
	int result;
	va_list args;

	DEBUG_CHECK_MAGIC(sfi, ECM_STATE_FILE_INSTANCE_MAGIC, "%p: magic failed", sfi);

	remain = ECM_STATE_FILE_BUFFER_SIZE - sfi->msg_len;
	ptr = sfi->msg + sfi->msg_len;
	result = snprintf(ptr, remain, "%s.%s=", sfi->prefix, name);
	if ((result < 0) || (result >= remain)) {
		return -1;
	}

	sfi->msg_len += result;
	remain -= result;
	ptr += result;

	va_start(args, fmt);
	result = vsnprintf(ptr, remain, fmt, args);
	va_end(args);
	if ((result < 0) || (result >= remain)) {
		return -2;
	}

	sfi->msg_len += result;
	remain -= result;
	ptr += result;

	result = snprintf(ptr, remain, "\n");
	if ((result < 0) || (result >= remain)) {
		return -3;
	}

	sfi->msg_len += result;
	return 0;
}
/*
 * ecm_state_char_device_read()
 *	Called to read the state
 */
static ssize_t ecm_state_char_device_read(struct file *file,	/* see include/linux/fs.h   */
			   char *buffer,				/* buffer to fill with data */
			   size_t length,				/* length of the buffer     */
			   loff_t *offset)				/* Doesn't apply - this is a char file */
{
	struct ecm_state_file_instance *sfi;
	int bytes_read = 0;						/* Number of bytes actually written to the buffer */
#ifdef ECM_DB_CTA_TRACK_ENABLE
	ecm_classifier_type_t ca_type;
#endif

	sfi = (struct ecm_state_file_instance *)file->private_data;
	DEBUG_CHECK_MAGIC(sfi, ECM_STATE_FILE_INSTANCE_MAGIC, "%p: magic failed", sfi);
	DEBUG_TRACE("%p: State read up to length %d bytes\n", sfi, length);

	/*
	 * If there is still some message remaining to be output then complete that first
	 */
	if (sfi->msg_len) {
		goto char_device_read_output;
	}

	if (sfi->ci) {
		struct ecm_db_connection_instance *cin;
		if (ecm_state_char_dev_conn_msg_prep(sfi)) {
			return -EIO;
		}

		/*
		 * Next connection for when we return
		 */
		cin = ecm_db_connection_get_and_ref_next(sfi->ci);
		ecm_db_connection_deref(sfi->ci);
		sfi->ci = cin;

		goto char_device_read_output;
	}

	if (sfi->mi) {
		struct ecm_db_mapping_instance *min;
		if (ecm_state_char_dev_mapping_msg_prep(sfi)) {
			return -EIO;
		}

		/*
		 * Next mapping for when we return
		 */
		min = ecm_db_mapping_get_and_ref_next(sfi->mi);
		ecm_db_mapping_deref(sfi->mi);
		sfi->mi = min;

		goto char_device_read_output;
	}

	if (sfi->hi) {
		struct ecm_db_host_instance *hin;
		if (ecm_state_char_dev_host_msg_prep(sfi)) {
			return -EIO;
		}

		/*
		 * Next host for when we return
		 */
		hin = ecm_db_host_get_and_ref_next(sfi->hi);
		ecm_db_host_deref(sfi->hi);
		sfi->hi = hin;

		goto char_device_read_output;
	}

	if (sfi->ni) {
		struct ecm_db_node_instance *nin;
		if (ecm_state_char_dev_node_msg_prep(sfi)) {
			return -EIO;
		}

		/*
		 * Next node for when we return
		 */
		nin = ecm_db_node_get_and_ref_next(sfi->ni);
		ecm_db_node_deref(sfi->ni);
		sfi->ni = nin;

		goto char_device_read_output;
	}

	if (sfi->ii) {
		struct ecm_db_iface_instance *iin;
		if (ecm_state_char_dev_iface_msg_prep(sfi)) {
			return -EIO;
		}

		/*
		 * Next iface for when we return
		 */
		iin = ecm_db_interface_get_and_ref_next(sfi->ii);
		ecm_db_iface_deref(sfi->ii);
		sfi->ii = iin;

		goto char_device_read_output;
	}

	if ((sfi->output_mask & ECM_STATE_FILE_OUTPUT_CONNECTIONS_CHAIN) && (sfi->connection_hash_index >= 0)) {
		if (ecm_state_char_dev_conn_chain_msg_prep(sfi)) {
			return -EIO;
		}
		sfi->connection_hash_index = ecm_db_connection_hash_index_get_next(sfi->connection_hash_index);
		goto char_device_read_output;
	}

	if ((sfi->output_mask & ECM_STATE_FILE_OUTPUT_MAPPINGS_CHAIN) && (sfi->mapping_hash_index >= 0)) {
		if (ecm_state_char_dev_mapping_chain_msg_prep(sfi)) {
			return -EIO;
		}
		sfi->mapping_hash_index = ecm_db_mapping_hash_index_get_next(sfi->mapping_hash_index);
		goto char_device_read_output;
	}

	if ((sfi->output_mask & ECM_STATE_FILE_OUTPUT_HOSTS_CHAIN) && (sfi->host_hash_index >= 0)) {
		if (ecm_state_char_dev_host_chain_msg_prep(sfi)) {
			return -EIO;
		}
		sfi->host_hash_index = ecm_db_host_hash_index_get_next(sfi->host_hash_index);
		goto char_device_read_output;
	}

	if ((sfi->output_mask & ECM_STATE_FILE_OUTPUT_NODES_CHAIN) && (sfi->node_hash_index >= 0)) {
		if (ecm_state_char_dev_node_chain_msg_prep(sfi)) {
			return -EIO;
		}
		sfi->node_hash_index = ecm_db_node_hash_index_get_next(sfi->node_hash_index);
		goto char_device_read_output;
	}

	if ((sfi->output_mask & ECM_STATE_FILE_OUTPUT_INTERFACES_CHAIN) && (sfi->iface_hash_index >= 0)) {
		if (ecm_state_char_dev_iface_chain_msg_prep(sfi)) {
			return -EIO;
		}
		sfi->iface_hash_index = ecm_db_iface_hash_index_get_next(sfi->iface_hash_index);
		goto char_device_read_output;
	}

	if ((sfi->output_mask & ECM_STATE_FILE_OUTPUT_PROTOCOL_COUNTS) && (sfi->protocol >= 0)) {
		if (ecm_state_char_dev_protocol_count_msg_prep(sfi)) {
			return -EIO;
		}
		sfi->protocol = ecm_db_protocol_get_next(sfi->protocol);
		goto char_device_read_output;
	}

#ifdef ECM_DB_CTA_TRACK_ENABLE
	for (ca_type = 0; ca_type < ECM_CLASSIFIER_TYPES; ++ca_type) {
		if (!sfi->classifier_type_assignments[ca_type]) continue;
		if (ecm_state_char_dev_cta_msg_prep(sfi, ca_type)) {
			return -EIO;
		}
		goto char_device_read_output;
	}
#endif

	/*
	 * EOF
	 */
	return 0;

char_device_read_output:

	/*
	 * If supplied buffer is small we limit what we output
	 */
	bytes_read = sfi->msg_len;
	if (bytes_read > length) {
		bytes_read = length;
	}
	if (copy_to_user(buffer, sfi->msgp, bytes_read)) {
		return -EIO;
	}
	sfi->msg_len -= bytes_read;
	sfi->msgp += bytes_read;

	DEBUG_TRACE("State read done, bytes_read %d bytes\n", bytes_read);

	/*
	 * Most read functions return the number of bytes put into the buffer
	 */
	return bytes_read;
}
Example #10
0
/*
 * ecm_tracker_tcp_init()
 *	Initialise the instance
 */
void ecm_tracker_tcp_init(struct ecm_tracker_tcp_instance *tti, int32_t data_limit, uint16_t mss_src_default, uint16_t mss_dest_default)
{
	struct ecm_tracker_tcp_internal_instance *ttii = (struct ecm_tracker_tcp_internal_instance *)tti;
	DEBUG_CHECK_MAGIC(ttii, ECM_TRACKER_TCP_INSTANCE_MAGIC, "%p: magic failed", ttii);
	DEBUG_TRACE("%p: init host addresses src mss: %d, dest mss: %d\n", ttii, mss_src_default, mss_dest_default);
}
Example #11
0
/*
 * ecm_tracker_tcp_state_update_callback()
 * 	Update connection state based on the knowledge we have and the skb given
 */
static void ecm_tracker_tcp_state_update_callback(struct ecm_tracker_instance *ti, ecm_tracker_sender_type_t sender, struct ecm_tracker_ip_header *ip_hdr, struct sk_buff *skb)
{
	struct ecm_tracker_tcp_internal_instance *ttii = (struct ecm_tracker_tcp_internal_instance *)ti;
	struct tcphdr tcp_hdr_buff;
	struct tcphdr *tcp_hdr;
	struct ecm_tracker_tcp_sender_state *sender_state;
	struct ecm_tracker_tcp_sender_state *peer_state;

	DEBUG_CHECK_MAGIC(ttii, ECM_TRACKER_TCP_INSTANCE_MAGIC, "%p: magic failed", ttii);

	/*
	 * Get refereces to states
	 */
	DEBUG_ASSERT((sender >= 0) && (sender <= 1), "%p: invalid sender %d\n", ttii, sender);
	sender_state = &ttii->sender_states[sender];
	peer_state = &ttii->sender_states[!sender];

	/*
	 * Get tcp header
	 */
	tcp_hdr = ecm_tracker_tcp_check_header_and_read(skb, ip_hdr, &tcp_hdr_buff);
	if (unlikely(!tcp_hdr)) {
		DEBUG_WARN("%p: no tcp_hdr for %p\n", ttii, skb);
		spin_lock_bh(&ttii->lock);
		sender_state->state = ECM_TRACKER_SENDER_STATE_FAULT;
		peer_state->state = ECM_TRACKER_SENDER_STATE_FAULT;
		spin_unlock_bh(&ttii->lock);
		return;
	}

	/*
	 * If either side reports a reset this is catastrophic for the connection
	 */
	if (unlikely(tcp_hdr->rst)) {
		DEBUG_INFO("%p: RESET\n", ttii);
		spin_lock_bh(&ttii->lock);
		sender_state->state = ECM_TRACKER_SENDER_STATE_FAULT;
		peer_state->state = ECM_TRACKER_SENDER_STATE_FAULT;
		spin_unlock_bh(&ttii->lock);
		return;
	}

	/*
	 * Likely ack is set - this constitutes the mainstay of a TCP connection
	 * The sending of an ack may put the other side of the connection into a different state
	 */
	spin_lock_bh(&ttii->lock);
	if (likely(tcp_hdr->ack)) {
		ecm_tracker_sender_state_t peer_state_current = peer_state->state;
		uint32_t ack_seq = ntohl(tcp_hdr->ack_seq);

		switch (peer_state_current) {
		case ECM_TRACKER_SENDER_STATE_UNKNOWN: {
			/*
			 * Looks like we came into this connection mid-flow.
			 * Flag that the peer is established which is all we can infer right now and
			 * initialise the peers SYN sequence for further analysis of sequence space.
			 */
			peer_state->state = ECM_TRACKER_SENDER_STATE_ESTABLISHED;
			peer_state->syn_seq = (ack_seq - 1);		/* -1 is because the ACK has ack'd our ficticious SYN */
			DEBUG_INFO("%p: From unkown to established, ack_seq: %u, syn_seq: %u\n", ttii, ack_seq, peer_state->syn_seq);
			break;
		}
		case ECM_TRACKER_SENDER_STATE_ESTABLISHING: {
			int32_t ackd;
			uint32_t syn_seq;

			syn_seq = peer_state->syn_seq;
			ackd = (int32_t)(ack_seq - syn_seq);
			DEBUG_TRACE("%p: ack %u for syn_seq %u? ackd = %d\n", ttii, ack_seq, syn_seq, ackd);

			if (ackd <= 0) {
				DEBUG_TRACE("%p: No change\n", ttii);
			} else {
				DEBUG_INFO("%p: Established\n", ttii);
				peer_state->state = ECM_TRACKER_SENDER_STATE_ESTABLISHED;
			}
			break;
		}
		case ECM_TRACKER_SENDER_STATE_CLOSING: {
			int32_t ackd;
			uint32_t fin_seq;

			fin_seq = peer_state->fin_seq;
			ackd = (int32_t)(ack_seq - fin_seq);
			DEBUG_TRACE("%p: ack %u for fin_seq %u? ackd = %d\n", ttii, ack_seq, fin_seq, ackd);

			if (ackd <= 0) {
				DEBUG_TRACE("%p: No change\n", ttii);
			} else {
				DEBUG_TRACE("%p: Closed\n", ttii);
				peer_state->state = ECM_TRACKER_SENDER_STATE_CLOSED;
			}
			break;
		}
		case ECM_TRACKER_SENDER_STATE_ESTABLISHED:
		case ECM_TRACKER_SENDER_STATE_CLOSED:
		case ECM_TRACKER_SENDER_STATE_FAULT:
			/*
			 * No change
			 */
			break;
		default:
			DEBUG_ASSERT(false, "%p: unhandled state: %d\n", ttii, peer_state_current);
		}
	}

	/*
	 * Handle control flags sent by the sender (SYN & FIN)
	 * Handle SYN first because, in sequence space, SYN is first.
	 */
	if (tcp_hdr->syn) {
		ecm_tracker_sender_state_t sender_state_current = sender_state->state;
		uint32_t seq = ntohl(tcp_hdr->seq);

		switch (sender_state_current) {
		case ECM_TRACKER_SENDER_STATE_UNKNOWN:
			sender_state->state = ECM_TRACKER_SENDER_STATE_ESTABLISHING;
			sender_state->syn_seq = seq;		/* Seq is the sequence number of the SYN */
			DEBUG_INFO("%p: From unkown to establishing, syn_seq: %u\n", ttii, sender_state->syn_seq);
			break;
		case ECM_TRACKER_SENDER_STATE_CLOSING:
		case ECM_TRACKER_SENDER_STATE_CLOSED:
			/*
			 * SYN after seeing a FIN?  FAULT!
			 */
			sender_state->state = ECM_TRACKER_SENDER_STATE_FAULT;
			DEBUG_INFO("%p: SYN after FIN - fault\n", ttii);
			break;
		case ECM_TRACKER_SENDER_STATE_ESTABLISHED:
			/*
			 * SYN when established is just a duplicate down to syn/ack timing subtleties
			 */
		case ECM_TRACKER_SENDER_STATE_ESTABLISHING:
		case ECM_TRACKER_SENDER_STATE_FAULT:
			/*
			 * No change
			 */
			break;
		default:
			DEBUG_ASSERT(false, "%p: unhandled state: %d\n", ttii, sender_state_current);
		}
	}

	if (tcp_hdr->fin) {
		ecm_tracker_sender_state_t sender_state_current = sender_state->state;
		uint32_t seq = ntohl(tcp_hdr->seq);

		switch (sender_state_current) {
		case ECM_TRACKER_SENDER_STATE_UNKNOWN:
			/*
			 * Looks like we joined mid-flow.
			 * Have to set up both SYN and FIN.
			 * NOTE: It's possible that SYN is in the same packet as FIN, account for that in the seq numbers
			 */
			sender_state->state = ECM_TRACKER_SENDER_STATE_CLOSING;
			if (tcp_hdr->syn) {
				sender_state->syn_seq = seq;
				sender_state->fin_seq = seq + 1;
			} else {
				sender_state->fin_seq = seq;		/* seq is the FIN sequence */
				sender_state->syn_seq = seq - 1;	/* Make a guess at what the SYN was */
			}
			DEBUG_INFO("%p: From unkown to closing, syn_seq: %u, fin_seq: %u\n", ttii, sender_state->syn_seq, sender_state->fin_seq);
			break;
		case ECM_TRACKER_SENDER_STATE_ESTABLISHED:
			/*
			 * Connection becomes closing.
			 */
			sender_state->state = ECM_TRACKER_SENDER_STATE_CLOSING;
			sender_state->fin_seq = seq;
			DEBUG_INFO("%p: From established to closing, fin_seq: %u\n", ttii, sender_state->fin_seq);
			break;
		case ECM_TRACKER_SENDER_STATE_ESTABLISHING: {
			int32_t newer;

			/*
			 * FIN while waiting for SYN to be acknowledged is possible but only if it
			 * it is in the same packet or later sequence space
			 */
			newer = (int32_t)(seq - sender_state->syn_seq);
			if (!tcp_hdr->syn || (newer <= 0)) {
				DEBUG_INFO("%p: From establishing to fault - odd FIN seen, syn: %u, syn_seq: %u, newer: %d\n",
						ttii, tcp_hdr->syn, sender_state->syn_seq, newer);
				sender_state->state = ECM_TRACKER_SENDER_STATE_FAULT;
			} else {
				uint32_t fin_seq = seq;
				if (tcp_hdr->syn) {
					fin_seq++;
				}
				sender_state->state = ECM_TRACKER_SENDER_STATE_CLOSING;
				DEBUG_INFO("%p: From establishing to closing, syn: %u, syn_seq: %u, fin_seq: %u\n",
						ttii, tcp_hdr->syn, sender_state->syn_seq, sender_state->fin_seq);
			}
			break;
		}
		case ECM_TRACKER_SENDER_STATE_CLOSING:
		case ECM_TRACKER_SENDER_STATE_CLOSED:
		case ECM_TRACKER_SENDER_STATE_FAULT:
			/*
			 * No change
			 */
			break;
		default:
			DEBUG_ASSERT(false, "%p: unhandled state: %d\n", ttii, sender_state_current);
		}
	}

	spin_unlock_bh(&ttii->lock);
}