Beispiel #1
0
int vc_REQUEST_handle_packet(struct vContext *C)
{
	struct VDgramConn *dgram_conn = CTX_current_dgram_conn(C);
	struct VPacket *r_packet = CTX_r_packet(C);

	/* Received packet has to have PAY flag (this packet have to be acknowledged),
	 * ACK flag (acknowledgment of request packet sent to the server) and
	 * SYN flag (it is handshake packet) */
	if(r_packet->header.flags == (PAY_FLAG|ACK_FLAG|SYN_FLAG) &&
			/* Does this packet acknowledge right ID? (ACK command has to be first command!) */
			r_packet->sys_cmd[0].cmd.id==CMD_ACK_ID &&
			r_packet->sys_cmd[0].ack_cmd.pay_id==dgram_conn->host_id)
	{
		/* Client state */
		dgram_conn->host_state = UDP_CLIENT_STATE_PARTOPEN;

		/* Save ID of received payload packet */
		dgram_conn->peer_id = r_packet->header.payload_id;

		/* Call callback functions for system commands */
		if(v_conn_dgram_handle_sys_cmds(C, UDP_CLIENT_STATE_REQUEST)!=1) {
			v_print_log(VRS_PRINT_WARNING, "Received packet with wrong system command(s)\n");
			return RECEIVE_PACKET_CORRUPTED;
		}

		/* Compute RWIN */
		dgram_conn->rwin_peer = r_packet->header.window << dgram_conn->rwin_peer_scale;
	}
	else {
		v_print_log(VRS_PRINT_WARNING, "Packet with corrupted header was dropped.\n");
		return RECEIVE_PACKET_CORRUPTED;
	}

	return RECEIVE_PACKET_SUCCESS;
}
Beispiel #2
0
/**
 * \brief Check if it is necessary to send acknowledgment packet?
 */
static int check_ack_nak_flag(struct vContext *C)
{
	struct VDgramConn *vconn = CTX_current_dgram_conn(C);
	struct VPacket *s_packet = CTX_s_packet(C);
	struct VPacket *last_r_packet = CTX_r_packet(C);
	int ret = 0;

	/* Is it necessary to acknowledge payload packet? Several conditions
	 * has to be accomplished */

	/* There has to be some ACK and NAK commands for sending */
	if( vconn->ack_nak.count > 0 ) {
		if(
			/* Piggy packing of AckNak packet to the payload packet */
			(s_packet->header.flags & PAY_FLAG)
				||
			/* Last received packet was payload packet (not only AckNak packet) ... */
			(
					(last_r_packet->header.flags & PAY_FLAG) &&
					(last_r_packet->acked == 0)
			)
		)
		{
			last_r_packet->acked = 1;
			ret = 1;
		}
	}

	return ret;
}
Beispiel #3
0
void vc_destroy_dtls_connection(struct vContext *C)
{
	struct VDgramConn *dgram_conn = CTX_current_dgram_conn(C);
	int ret;

	v_print_log(VRS_PRINT_DEBUG_MSG, "Try to shut down DTLS connection.\n");

again:
	ret = SSL_shutdown(dgram_conn->io_ctx.ssl);
	if(ret!=1) {
		int error = SSL_get_error(dgram_conn->io_ctx.ssl, ret);
		switch(error) {
			case SSL_ERROR_WANT_READ:
			case SSL_ERROR_WANT_WRITE:
				goto again;
			default:
				v_print_log(VRS_PRINT_DEBUG_MSG, "DTLS connection was not able to shut down: %d\n", error);
				break;
		}
	} else {
		v_print_log(VRS_PRINT_DEBUG_MSG, "DTLS connection was shut down.\n");
	}

	SSL_free(dgram_conn->io_ctx.ssl);
	dgram_conn->io_ctx.ssl = NULL;
	dgram_conn->io_ctx.bio = NULL;
}
Beispiel #4
0
/**
 * \brief This function check if it is necessary to send payload packet
 */
static int check_pay_flag(struct vContext *C)
{
	struct VSession *vsession = CTX_current_session(C);
	struct VDgramConn *vconn = CTX_current_dgram_conn(C);
	struct timeval tv;
	int ret = 0;

	/* When there are no payload data to send, then there is possibly need
	 * for sending keep alive packet. */
	if(v_out_queue_get_count(vsession->out_queue) > 0) {
		ret = 1;
	} else {
		long int d_timeout, d_sec_timeout, d_usec_timeout;

		/* Get current time */
		gettimeofday(&tv, NULL);

		/* When was sent last payload packet? */
		d_sec_timeout = tv.tv_sec - vconn->tv_pay_send.tv_sec;
		d_usec_timeout = tv.tv_usec - vconn->tv_pay_send.tv_usec;
		d_timeout = 1000000*d_sec_timeout + d_usec_timeout;

		/* Is it necessary to send keep alive packet? */
		if(d_timeout > RESEND_TIMEOUT) {
			ret = 2;
		}
	}

	return ret;
}
Beispiel #5
0
int vc_PARTOPEN_handle_packet(struct vContext *C)
{
	struct VDgramConn *dgram_conn = CTX_current_dgram_conn(C);
	struct VPacket *r_packet = CTX_r_packet(C);

	/* Verse packet header has to include some flags and verse packet has to contain ack command */
	if(r_packet->header.flags == (PAY_FLAG|ACK_FLAG|ANK_FLAG) &&
			/* Payload ID has to be incremented */
			r_packet->header.payload_id == dgram_conn->peer_id+1 &&
			/* Does this packet contain ANK ID of last acknowledged Payload ID? */
			r_packet->header.ank_id == dgram_conn->peer_id &&
			/* Does this packet acknowledge right ID? (ACK command has to be first command!) */
			r_packet->sys_cmd[0].cmd.id == CMD_ACK_ID &&
			r_packet->sys_cmd[0].ack_cmd.pay_id == dgram_conn->host_id+1 )
	{
		/* Client state */
		dgram_conn->host_state = UDP_CLIENT_STATE_OPEN;

		/* Call callback functions for system commands */
		if(v_conn_dgram_handle_sys_cmds(C, UDP_CLIENT_STATE_PARTOPEN) != 1) {
			v_print_log(VRS_PRINT_WARNING, "Received packet with wrong system command(s)\n");
			return RECEIVE_PACKET_CORRUPTED;
		}

		/* Compute RWIN */
		dgram_conn->rwin_peer = r_packet->header.window << dgram_conn->rwin_peer_scale;
	} else {
		if(is_log_level(VRS_PRINT_WARNING)) v_print_log(VRS_PRINT_WARNING, "Packet with bad header was dropped.\n");
		return RECEIVE_PACKET_CORRUPTED;
	}

	return RECEIVE_PACKET_SUCCESS;
}
Beispiel #6
0
int vc_CLOSING_handle_packet(struct vContext *C)
{
	struct VDgramConn *dgram_conn = CTX_current_dgram_conn(C);
	struct VPacket *r_packet = CTX_r_packet(C);

	/* Received packet has to have PAY flag (this packet have to be acknowledged),
	 * ACK flag (it has to acknowledge close request) and FIN flag (it is
	 * teardown packet) */
	if(r_packet->header.flags == (PAY_FLAG|ACK_FLAG|FIN_FLAG) &&
		/* Does this packet acknowledge right ID? (ACK command has to be first command!) */
			r_packet->sys_cmd[0].cmd.id==CMD_ACK_ID &&
			r_packet->sys_cmd[0].ack_cmd.pay_id==dgram_conn->host_id + dgram_conn->count_s_pay)
	{
		/* Client state */
		dgram_conn->host_state = UDP_CLIENT_STATE_CLOSED;

		/* Call callback functions for system commands */
		v_conn_dgram_handle_sys_cmds(C, UDP_CLIENT_STATE_CLOSING);

		/* Compute RWIN */
		dgram_conn->rwin_peer = r_packet->header.window << dgram_conn->rwin_peer_scale;
	}
	else {
		if(is_log_level(VRS_PRINT_WARNING)) v_print_log(VRS_PRINT_WARNING, "Packet with corrupted header was dropped.\n");
		return RECEIVE_PACKET_CORRUPTED;
	}

	return RECEIVE_PACKET_SUCCESS;
}
Beispiel #7
0
void vc_REQUEST_init_send_packet(struct vContext *C)
{
	struct VC_CTX *vc_ctx = CTX_client_ctx(C);
	struct VDgramConn *dgram_conn = CTX_current_dgram_conn(C);
	struct VSession *vsession = CTX_current_session(C);
	struct VPacket *s_packet = CTX_s_packet(C);
	int cmd_rank = 0;
	static const uint8 cc_none = CC_NONE,
			cmpr_none = CMPR_NONE,
			cmpr_addr_share = CMPR_ADDR_SHARE;

	/* Verse packet header */
	s_packet->header.version = 1;
	s_packet->header.flags = PAY_FLAG | SYN_FLAG;
	s_packet->header.window = dgram_conn->rwin_host >> dgram_conn->rwin_host_scale;
	s_packet->header.payload_id = dgram_conn->host_id;
	s_packet->header.ack_nak_id = 0;
	s_packet->header.ank_id = 0;

	/* System commands */
	if(vsession->host_token.str!=NULL) {
		/* Add negotiated token */
		cmd_rank += v_add_negotiate_cmd(s_packet->sys_cmd, cmd_rank,
				CMD_CHANGE_L_ID, FTR_TOKEN, vsession->host_token.str, NULL);
	}

	/* Add CC (local) proposal */
	cmd_rank += v_add_negotiate_cmd(s_packet->sys_cmd, cmd_rank,
			CMD_CHANGE_L_ID, FTR_CC_ID, &cc_none, NULL);

	/* Add CC (remote) proposal */
	cmd_rank += v_add_negotiate_cmd(s_packet->sys_cmd, cmd_rank,
			CMD_CHANGE_R_ID, FTR_CC_ID, &cc_none, NULL);

	/* Add Scale Window proposal */
	cmd_rank += v_add_negotiate_cmd(s_packet->sys_cmd, cmd_rank,
			CMD_CHANGE_L_ID, FTR_RWIN_SCALE, &vc_ctx->rwin_scale, NULL);

	/* Add command compression proposal */
	if(vsession->flags & VRS_CMD_CMPR_NONE) {
		/* Client doesn't want to send compressed commands (local proposal) */
		cmd_rank += v_add_negotiate_cmd(s_packet->sys_cmd, cmd_rank,
				CMD_CHANGE_L_ID, FTR_CMD_COMPRESS, &cmpr_none, NULL);
		/* Client isn't able to receive compressed commands (remote proposal) */
		cmd_rank += v_add_negotiate_cmd(s_packet->sys_cmd, cmd_rank,
				CMD_CHANGE_R_ID, FTR_CMD_COMPRESS, &cmpr_none, NULL);
	} else {
		/* Client wants to send compressed commands (local proposal) */
		cmd_rank += v_add_negotiate_cmd(s_packet->sys_cmd, cmd_rank,
				CMD_CHANGE_L_ID, FTR_CMD_COMPRESS, &cmpr_addr_share, NULL);
		/* Client is able to receive compressed commands (remote proposal) */
		cmd_rank += v_add_negotiate_cmd(s_packet->sys_cmd, cmd_rank,
				CMD_CHANGE_R_ID, FTR_CMD_COMPRESS, &cmpr_addr_share, NULL);
	}
}
Beispiel #8
0
int vc_CLOSING_loop(struct vContext *C)
{
	struct VC_CTX *vc_ctx = CTX_client_ctx(C);
	struct VDgramConn *dgram_conn = CTX_current_dgram_conn(C);
	int ret;

	/* CLOSING state */
	if(is_log_level(VRS_PRINT_DEBUG_MSG)) {
		printf("%c[%d;%dm", 27, 1, 31);
		v_print_log(VRS_PRINT_DEBUG_MSG, "Client state: CLOSING\n");
		printf("%c[%dm", 27, 0);
	}

	dgram_conn->state[UDP_CLIENT_STATE_CLOSING].attempts = 0;

	while(dgram_conn->state[UDP_CLIENT_STATE_CLOSING].attempts < vc_ctx->max_connection_attempts) {
		/* Send closing packet */
		if( vc_CLOSING_send_packet(C) == SEND_PACKET_ERROR) {
			return STATE_EXIT_ERROR;
		}
		/* Try to receive packet and handle received packet */
		ret = vc_receive_and_handle_packet(C, vc_CLOSING_handle_packet);
		if(ret == RECEIVE_PACKET_SUCCESS) {
			break;			/* Break loop and receive to the next state */
		} else if(ret == RECEIVE_PACKET_TIMEOUT) {
			dgram_conn->state[UDP_CLIENT_STATE_CLOSING].attempts++;		/* No packet receive ... try it again */
		} else if(ret == RECEIVE_PACKET_CORRUPTED) {
			dgram_conn->state[UDP_CLIENT_STATE_CLOSING].attempts++;		/* Corrupted packet received ... try it again */
		} else if(ret == RECEIVE_PACKET_FAKED) {
			continue;		/* Packet wasn't received from the server (should not happen, because connect()) */
		} else if(ret == RECEIVE_PACKET_ERROR) {
			return STATE_EXIT_ERROR;
		}

#ifdef WITH_OPENSSL
		if(dgram_conn->io_ctx.flags & SOCKET_SECURED) {
			/* Did server close DTLS connection? */
			if((SSL_get_shutdown(dgram_conn->io_ctx.ssl) & SSL_RECEIVED_SHUTDOWN)) {
				return STATE_EXIT_ERROR;
			}
		}
#endif
	}

	if(dgram_conn->host_state == UDP_CLIENT_STATE_CLOSING) {
		if(is_log_level(VRS_PRINT_ERROR)) v_print_log(VRS_PRINT_ERROR,
				"Maximum count of teardown attempts reached %d.\n", vc_ctx->max_connection_attempts);
		return STATE_EXIT_ERROR;
	}

	return STATE_EXIT_SUCCESS;
}
Beispiel #9
0
/**
 * \brief Call callback functions for all received system commands. Each state
 * could have different callback function that is stored in VConnection
 * structure.
 * \param[in]	*C			The Verse context;
 * \param[in]	state_id	The id of the current state
 * \return		The function returns 1, when all system commands were processes
 * without problem and it returns 0, when some error occurs.
 */
int v_conn_dgram_handle_sys_cmds(struct vContext *C, const unsigned short state_id)
{
	struct VDgramConn *dgram_conn = CTX_current_dgram_conn(C);
	struct VPacket *r_packet = CTX_r_packet(C);
	int cmd_rank, r, ret = 1;

	/* Parse system commands (not ACK and NAK messages ... these are proccessed separately) */
	for(cmd_rank=0;
			r_packet->sys_cmd[cmd_rank].cmd.id != CMD_RESERVED_ID &&
					cmd_rank < MAX_SYSTEM_COMMAND_COUNT;
			cmd_rank++)
	{
		switch (r_packet->sys_cmd[cmd_rank].cmd.id) {
		case CMD_ACK_ID:
			break;
		case CMD_NAK_ID:
			break;
		case CMD_CHANGE_L_ID:
			if(dgram_conn->state[state_id].CHANGE_L_cb) {
				r = dgram_conn->state[state_id].CHANGE_L_cb(C, &r_packet->sys_cmd[cmd_rank].cmd);
				ret = (ret==0)?0:r;
			}
			break;
		case CMD_CHANGE_R_ID:
			if(dgram_conn->state[state_id].CHANGE_R_cb) {
				r = dgram_conn->state[state_id].CHANGE_R_cb(C, &r_packet->sys_cmd[cmd_rank].cmd);
				ret = (ret==0)?0:r;
			}
			break;
		case CMD_CONFIRM_L_ID:
			if(dgram_conn->state[state_id].CONFIRM_L_cb) {
				r = dgram_conn->state[state_id].CONFIRM_L_cb(C, &r_packet->sys_cmd[cmd_rank].cmd);
				ret = (ret==0)?0:r;
			}
			break;
		case CMD_CONFIRM_R_ID:
			if(dgram_conn->state[state_id].CONFIRM_R_cb) {
				r = dgram_conn->state[state_id].CONFIRM_R_cb(C, &r_packet->sys_cmd[cmd_rank].cmd);
				ret = (ret==0)?0:r;
			}
			break;
		default:
			/* Unknown command ID */
			if(is_log_level(VRS_PRINT_WARNING)) {
				v_print_log(VRS_PRINT_WARNING, "Unknown system command ID: %d\n", r_packet->sys_cmd[cmd_rank].cmd.id);
			}
			break;
		}
	}

	return ret;
}
Beispiel #10
0
int vc_send_packet(struct vContext *C)
{
	struct VDgramConn *dgram_conn = CTX_current_dgram_conn(C);
	struct IO_CTX *io_ctx = CTX_io_ctx(C);
	struct VPacket *s_packet = CTX_s_packet(C);
	int error_num;
	struct timeval tv;
	unsigned short buffer_pos = 0;
	int ret;

	v_print_send_packet(C);

	/* Fill buffer */
	buffer_pos += v_pack_packet_header(s_packet, &io_ctx->buf[buffer_pos]);
	buffer_pos += v_pack_dgram_system_commands(s_packet, &io_ctx->buf[buffer_pos]);
	io_ctx->buf_size = buffer_pos;

	/* Send buffer */
	ret = v_send_packet(io_ctx, &error_num);

	if(ret == SEND_PACKET_SUCCESS) {

		/* Update time of sending last packet */
		gettimeofday(&tv, NULL);
		dgram_conn->tv_pay_send.tv_sec = tv.tv_sec;
		dgram_conn->tv_pay_send.tv_usec = tv.tv_usec;

		/* Update counter of sent packets */
		switch(dgram_conn->host_state) {
		case UDP_CLIENT_STATE_REQUEST:
			dgram_conn->count_s_pay = 1;
			dgram_conn->count_s_ack = 0;
			break;
		case UDP_CLIENT_STATE_PARTOPEN:
			dgram_conn->count_s_pay = 2;
			dgram_conn->count_s_ack = 1;
			break;
		case UDP_CLIENT_STATE_OPEN:
			dgram_conn->count_s_pay++;
			dgram_conn->count_s_ack++;
			break;
		case UDP_CLIENT_STATE_CLOSING:
			break;
		case UDP_CLIENT_STATE_CLOSED:
			break;
		}
	}

	return ret;
}
Beispiel #11
0
/**
 * \brief This function is the callback function for received Change_R
 * commands in REQUEST state.
 */
static int vc_REQUEST_CHANGE_R_cb(struct vContext *C, struct Generic_Cmd *cmd)
{
	struct VDgramConn *dgram_conn = CTX_current_dgram_conn(C);
	struct Negotiate_Cmd *change_r_cmd = (struct Negotiate_Cmd*)cmd;
	int value_rank;

	if(change_r_cmd->feature == FTR_FC_ID) {
		int ret = 0;

		for(value_rank = 0; value_rank < change_r_cmd->count; value_rank++) {
			/* Is value in "list" of supported methods */
			if(change_r_cmd->value[value_rank].uint8 == FC_NONE ||
					change_r_cmd->value[value_rank].uint8 == FC_TCP_LIKE)
			{
				/* It will try to use first found supported method, but ... */
				if(dgram_conn->fc_meth == FC_RESERVED) {
					/* Flow Control has not been proposed yet */
				} else {
					/* Server has to propose same FC methods for client
					 * and server. Client can't use different method then
					 * server and vice versa. */
					if(dgram_conn->fc_meth != change_r_cmd->value[value_rank].uint8) {
						v_print_log(VRS_PRINT_WARNING,
								"Skipping proposed remote FC :%d is not the same as proposed local FC: %d\n",
								change_r_cmd->value[value_rank].uint8,
								dgram_conn->fc_meth);
						continue;
					}
				}
				dgram_conn->fc_meth = change_r_cmd->value[value_rank].uint8;
				v_print_log(VRS_PRINT_DEBUG_MSG,
						"Remote Flow Control ID: %d proposed\n",
						change_r_cmd->value[0].uint8);
				ret = 1;
				break;
			} else {
				v_print_log(VRS_PRINT_ERROR,
						"Skipping unsupported Flow Control method: %d\n",
						change_r_cmd->value[value_rank].uint8);
				continue;
			}
		}

		return ret;
	}

	/* Ignore unknown feature */
	return 1;
}
Beispiel #12
0
void vc_PARTOPEN_init_send_packet(struct vContext *C)
{
	struct VSession *vsession = CTX_current_session(C);
	struct VDgramConn *dgram_conn = CTX_current_dgram_conn(C);
	struct VPacket *s_packet = CTX_s_packet(C);
	struct VPacket *r_packet = CTX_r_packet(C);
	int cmd_rank = 0;

	/* Verse packet header */
	s_packet->header.version = 1;
	s_packet->header.flags = PAY_FLAG | ACK_FLAG ;
	s_packet->header.window = dgram_conn->rwin_host >> dgram_conn->rwin_host_scale;
	s_packet->header.payload_id = dgram_conn->host_id + 1;
	s_packet->header.ack_nak_id = 0;
	if(r_packet->sys_cmd[0].cmd.id == CMD_ACK_ID) {
		s_packet->header.flags |= ANK_FLAG;
		s_packet->header.ank_id = r_packet->sys_cmd[0].ack_cmd.pay_id;
		dgram_conn->ank_id = r_packet->sys_cmd[0].ack_cmd.pay_id;
	} else {
		s_packet->header.ank_id = 0;
	}

	/* System commands */
	s_packet->sys_cmd[cmd_rank].ack_cmd.id = CMD_ACK_ID;
	s_packet->sys_cmd[cmd_rank].ack_cmd.pay_id = dgram_conn->peer_id;
	cmd_rank++;

	/* Confirm proposed TOKEN */
	if(vsession->peer_token.str!=NULL) {
		cmd_rank += v_add_negotiate_cmd(s_packet->sys_cmd, cmd_rank,
				CMD_CONFIRM_L_ID, FTR_TOKEN, vsession->peer_token.str, NULL);
	}

	/* Confirm proposed Flow Control ID */
	if(dgram_conn->fc_meth != FC_RESERVED) {
		cmd_rank += v_add_negotiate_cmd(s_packet->sys_cmd, cmd_rank,
				CMD_CONFIRM_L_ID, FTR_FC_ID, &dgram_conn->fc_meth, NULL);
		cmd_rank += v_add_negotiate_cmd(s_packet->sys_cmd, cmd_rank,
				CMD_CONFIRM_R_ID, FTR_FC_ID, &dgram_conn->fc_meth, NULL);
	}

	/* Confirm proposed shifting of Flow Control Window */
	if(dgram_conn->rwin_peer_scale != 0) {
		cmd_rank += v_add_negotiate_cmd(s_packet->sys_cmd, cmd_rank,
				CMD_CONFIRM_L_ID, FTR_RWIN_SCALE, &dgram_conn->rwin_peer_scale, NULL);
	}
}
Beispiel #13
0
void vc_CLOSING_init_send_packet(struct vContext *C)
{
	struct VDgramConn *dgram_conn = CTX_current_dgram_conn(C);
	struct VPacket *s_packet = CTX_s_packet(C);

	/* Verse packet header */
	s_packet->header.version = 1;
	s_packet->header.flags = PAY_FLAG | FIN_FLAG;
	s_packet->header.window = dgram_conn->rwin_host >> dgram_conn->rwin_host_scale;
	s_packet->header.payload_id = dgram_conn->host_id + dgram_conn->count_s_pay;
	s_packet->header.ack_nak_id = dgram_conn->count_s_ack;
	s_packet->header.ank_id = 0;

	/* System commands */
	/* TODO: send ack and nak commands */
	s_packet->sys_cmd[0].cmd.id = CMD_RESERVED_ID;
}
Beispiel #14
0
void v_print_send_packet(struct vContext *C)
{
	struct IO_CTX *io_ctx = CTX_io_ctx(C);
	struct VDgramConn *dgram_conn = CTX_current_dgram_conn(C);
	struct VPacket *s_packet = CTX_s_packet(C);

	if(is_log_level(VRS_PRINT_DEBUG_MSG)) {
		printf("%c[%d;%dm", 27, 1, 32);
		v_print_log(VRS_PRINT_DEBUG_MSG, "Send packet: ");
		v_print_log_simple(VRS_PRINT_DEBUG_MSG, "Socket: %d, ", io_ctx->sockfd);
		v_print_addr_port(VRS_PRINT_DEBUG_MSG, &io_ctx->peer_addr);
		v_print_packet_header(VRS_PRINT_DEBUG_MSG, s_packet);
		v_print_log(VRS_PRINT_DEBUG_MSG, "Shift: %d -> Window: %d\n", dgram_conn->rwin_host_scale, (unsigned int)s_packet->header.window << dgram_conn->rwin_host_scale);
		v_print_packet_sys_cmds(VRS_PRINT_DEBUG_MSG, s_packet);
		printf("%c[%dm", 27, 0);
	}
}
Beispiel #15
0
/**
 * \brief This function set size of congestion window (congestion control)
 */
static void set_host_cwin(struct vContext *C)
{
	struct VDgramConn *dgram_conn = CTX_current_dgram_conn(C);

	switch(dgram_conn->cc_meth) {
	case CC_NONE:
		dgram_conn->cwin = 0xFFFF;
		break;
	case CC_TCP_LIKE:
		/* TODO: implement real TCP like congestion control */
		dgram_conn->cwin = 0xFFFF;
		break;
	case CC_RESERVED:
		v_print_log(VRS_PRINT_WARNING, "CC_RESERVED should never be used\n");
		dgram_conn->cwin = 0xFFFF;
		break;
	}
}
Beispiel #16
0
/**
 * \brief This function is the callback function for received Confirm_R
 * commands in REQUEST state.
 */
static int vc_REQUEST_CONFIRM_R_cb(struct vContext *C, struct Generic_Cmd *cmd)
{
	struct VDgramConn *dgram_conn = CTX_current_dgram_conn(C);
	struct Negotiate_Cmd *confirm_r_cmd = (struct Negotiate_Cmd*)cmd;

	/* Server should confirm client proposal of Congestion Control (remote) */
	if(confirm_r_cmd->feature == FTR_CC_ID) {
		/* TODO: better implementation */
		if(confirm_r_cmd->count == 1 &&	/* Any confirm command has to include only one value */
				confirm_r_cmd->value[0].uint8 == CC_NONE) /* "List" of supported methods */
		{
			v_print_log(VRS_PRINT_DEBUG_MSG, "Remote Congestion Control ID: %d confirmed\n",
					confirm_r_cmd->value[0].uint8);
			dgram_conn->cc_meth = CC_NONE;
			return 1;
		} else {
			v_print_log(VRS_PRINT_ERROR, "Unsupported Congestion Control\n");
			return 0;
		}
	}

	/* Server should confirm client proposal of command compression */
	if(confirm_r_cmd->feature == FTR_CMD_COMPRESS) {
		if(confirm_r_cmd->count == 1) {
			if(confirm_r_cmd->value[0].uint8 == CMPR_NONE ||
					confirm_r_cmd->value[0].uint8 == CMPR_ADDR_SHARE)
			{
				v_print_log(VRS_PRINT_DEBUG_MSG, "Remote Command Compression: %d confirmed\n",
						confirm_r_cmd->value[0].uint8);
				dgram_conn->peer_cmd_cmpr = confirm_r_cmd->value[0].uint8;
				return 1;
			} else {
				v_print_log(VRS_PRINT_ERROR, "Unsupported Command Compress\n");
				return 0;
			}
		}
	}

	return 1;
}
Beispiel #17
0
/**
 * \brief This function handles node commands, when payload packet was received.
 *
 * This function also detects packet loss of previous not received payload
 * packets. All node commands are unpacked from the buffer and added to incoming queue.
 *
 * \param[in]	*C	The verse context.
 *
 * \return		This function returns RECEIVE_PACKET_DELAYED, when delayd packet
 * was received, otherwise it returns RECEIVE_PACKET_SUCCESS.
 */
static int handle_node_commands(struct vContext *C)
{
	struct VSession *vsession = CTX_current_session(C);
	struct VDgramConn *vconn = CTX_current_dgram_conn(C);
	struct VPacket *r_packet = CTX_r_packet(C);
	struct Ack_Nak_Cmd ack_nak_cmd;

	/* Note: when ACK and NAK command are add to the AckNak history,
	 * then this vector of commands is automatically compressed. */

	/* Was any packet lost since last receiving of packet? */
	if(r_packet->header.payload_id > vconn->last_r_pay+1) {
		v_print_log(VRS_PRINT_DEBUG_MSG, "Packet(s) lost: %d - %d\n",
				vconn->last_r_pay+1, r_packet->header.payload_id-1);
		/* Add NAK command to the list of ACK NAK commands */
		ack_nak_cmd.id = CMD_NAK_ID;
		ack_nak_cmd.pay_id = vconn->last_r_pay+1;
		v_ack_nak_history_add_cmd(&vconn->ack_nak, &ack_nak_cmd);
	/* Was some delayed packet received? */
	} else if(r_packet->header.payload_id < vconn->last_r_pay+1) {
		if(is_log_level(VRS_PRINT_WARNING))
			v_print_log(VRS_PRINT_WARNING, "Received unordered packet: %d, expected: %d\n", r_packet->header.payload_id, vconn->last_r_pay+1);
		/* Drop this packet */
		return RECEIVE_PACKET_UNORDERED;
	}

	/* ADD ACK command to the list of ACK NAK commands */
	ack_nak_cmd.id = CMD_ACK_ID;
	ack_nak_cmd.pay_id = r_packet->header.payload_id;
	v_ack_nak_history_add_cmd(&vconn->ack_nak, &ack_nak_cmd);

	/* Check if there are really node commands */
	if(r_packet->data!=NULL) {
		/* Unpack node commands and put them to the queue of incoming commands */
		v_cmd_unpack((char*)r_packet->data, r_packet->data_size, vsession->in_queue);
	}

	return RECEIVE_PACKET_SUCCESS;
}
Beispiel #18
0
/**
 * \brief This function set size of receive window (flow control), which is sent
 * to the peer.
 *
 * This function sets C->dgram_conn->rwin_host
 *
 * \param[in]	*C	Verse context
 *
 */
static void set_host_rwin(struct vContext *C)
{
	struct VDgramConn *dgram_conn = CTX_current_dgram_conn(C);
	struct VSession *vsession = CTX_current_session(C);
	long int free_space;

	switch(dgram_conn->fc_meth) {
	case FC_NONE:
		dgram_conn->rwin_host = 0xFFFFFFFF;
		break;
	case FC_TCP_LIKE:
		/* Compute free space in incomming queue */
		free_space = (long int)vsession->in_queue->max_size - (long int)vsession->in_queue->size;
		dgram_conn->rwin_host = (uint32)(free_space > 0) ? free_space : 0;
		/*printf(">>> max_size: %d, size: %d -> free_size: %ld -> window %d<<<\n",
				vsession->in_queue->max_size, vsession->in_queue->size, free_space, dgram_conn->rwin_host);*/
		break;
	case FC_RESERVED:
		v_print_log(VRS_PRINT_WARNING, "FC_RESERVED should never be used\n");
		dgram_conn->rwin_host = 0xFFFFFFFF;
		break;
	}
}
Beispiel #19
0
/**
 * \brief This function removes packet with id from history of sent packets.
 * It removes all its commands from the command history too.
 *
 * \param[in]	*C		The verse context.
 * \param[in]	id		The ID of packet, the will be removed from the history.
 *
 * \return		This function returns 1, then packet with id was found in the history
 * and it returns 0 otherwise.
 */
int v_packet_history_rem_packet(struct vContext *C, uint32 id)
{
	struct VS_CTX *vs_ctx = CTX_server_ctx(C);
	struct VDgramConn *dgram_conn = CTX_current_dgram_conn(C);
	struct VPacket_History *history = &dgram_conn->packet_history;
	struct VPacket *r_packet = CTX_r_packet(C);
	struct VSent_Packet *sent_packet;
	int ret = 0, is_fake_cmd_received = 0;

	sent_packet = v_packet_history_find_packet(history, id);

	if(sent_packet != NULL) {
		struct VSent_Command *sent_cmd;

		v_print_log(VRS_PRINT_DEBUG_MSG, "Removing packet: %d from history\n", sent_packet->id);

		/* Go through the whole list of sent commands and free them from the
		 * hashed linked list */
		sent_cmd = sent_packet->cmds.first;
		while(sent_cmd != NULL) {
			/* Remove own command from hashed linked list if it wasn't already
			 * removed, when command was obsoleted by some newer packet */
			if(sent_cmd->vbucket!=NULL) {
				struct Generic_Cmd *cmd = (struct Generic_Cmd*)sent_cmd->vbucket->data;

				/* Bucket has to include some data */
				assert(sent_cmd->vbucket->data!=NULL);

				/* Decrease total size of commands that were sent and wasn't acknowladged yet*/
				dgram_conn->sent_size -= v_cmd_size(cmd);

				/* Put fake command for create/destroy commands at verse server */
				if(vs_ctx != NULL) {
					struct VSession *vsession = CTX_current_session(C);
					struct Generic_Cmd *fake_cmd = NULL;

					switch(cmd->id) {
					case CMD_NODE_CREATE:
						fake_cmd = v_fake_node_create_ack_create(UINT32(cmd->data[UINT16_SIZE + UINT32_SIZE]));
						break;
					case CMD_NODE_DESTROY:
						fake_cmd = v_fake_node_destroy_ack_create(UINT32(cmd->data[0]));
						break;
					case CMD_TAGGROUP_CREATE:
						fake_cmd = v_fake_taggroup_create_ack_create(UINT32(cmd->data[0]),
								UINT16(cmd->data[UINT32_SIZE]));
						break;
					case CMD_TAGGROUP_DESTROY:
						fake_cmd = v_fake_taggroup_destroy_ack_create(UINT32(cmd->data[0]),
								UINT16(cmd->data[UINT32_SIZE]));
						break;
					case CMD_TAG_CREATE:
						fake_cmd = v_tag_create_ack_create(UINT32(cmd->data[0]),
								UINT16(cmd->data[UINT32_SIZE]),
								UINT16(cmd->data[UINT32_SIZE + UINT16_SIZE]));
						break;
					case CMD_TAG_DESTROY:
						fake_cmd = v_tag_destroy_ack_create(UINT32(cmd->data[0]),
								UINT16(cmd->data[UINT32_SIZE]),
								UINT16(cmd->data[UINT32_SIZE + UINT16_SIZE]));
						break;
					case CMD_LAYER_CREATE:
						fake_cmd = v_fake_layer_create_ack_create(UINT32(cmd->data[0]),
								UINT16(cmd->data[UINT32_SIZE + UINT16_SIZE]));
						break;
					case CMD_LAYER_DESTROY:
						fake_cmd = v_fake_layer_destroy_ack_create(UINT32(cmd->data[0]),
								UINT16(cmd->data[UINT32_SIZE]));
						break;
					default:
						break;
					}

					if(fake_cmd != NULL) {
						is_fake_cmd_received = 1;
						/* Push command to the queue of incomming commands */
						v_in_queue_push(vsession->in_queue, fake_cmd);
						/* Print content of fake command */
						v_fake_cmd_print(VRS_PRINT_DEBUG_MSG, fake_cmd);
					}
				}

				/* Remove command from the history of sent commands */
				ret = v_hash_array_remove_item(&history->cmd_hist[sent_cmd->id]->cmds, sent_cmd->vbucket->data);

				if(ret == 1) {
					/* Destroy command */
					v_cmd_destroy(&cmd);
				} else {
					v_print_log(VRS_PRINT_ERROR, "Unable to remove command id: %d\n", sent_cmd->id);
					ret = 0;
				}
			}
			sent_cmd = sent_cmd->next;
		}

		/* Free linked list of sent commands */
		v_list_free(&sent_packet->cmds);

		/* Remove packet itself from the linked list of sent packet */
		v_list_rem_item(&history->packets, sent_packet);
		free(sent_packet);

		ret = 1;
	} else {
		/* When acknowledged payload packet is not found in history, then it
		 * is OK, because it is probably keep alive packet without any node
		 * commands */
		v_print_log(VRS_PRINT_DEBUG_MSG, "Packet with id: %d, not found in history\n", id);
	}

	/* When pure ack packet caused adding some fake commands to the queue, then
	 * poke server data thread */
	if( (vs_ctx != NULL) && (is_fake_cmd_received == 1) && (r_packet->data == NULL)) {
		sem_post(vs_ctx->data.sem);
	}

	return ret;
}
Beispiel #20
0
/**
 * \brief This function packs and compress command to the packet from one
 * priority queue.
 *
 * \param[in]	*C	The verse context
 * \param[in]	*sent_packet	The pointer at structure with send packet
 * \param[in]	buffer_pos		The curent size of buffer of sent packet
 * \param[in]	prio			The priority of sub queue
 * \param[in]	prio_win		The window size of current prio queue
 * \param[out]	tot_cmd_size	The total size of commands that were poped from prio queue
 */
static int pack_prio_queue(struct vContext *C,
		struct VSent_Packet *sent_packet,
		int buffer_pos,
		uint8 prio,
		uint16 prio_win,
		uint16 *tot_cmd_size)
{
	struct VSession *vsession = CTX_current_session(C);
	struct VDgramConn *vconn = CTX_current_dgram_conn(C);
	struct IO_CTX *io_ctx = CTX_io_ctx(C);
	struct Generic_Cmd *cmd;
	int ret, last_cmd_count = 0;
	uint16 cmd_count, cmd_len, cmd_size, sum_len=0;
	int8 cmd_share;
	uint8  last_cmd_id = CMD_RESERVED_ID;

	while( (v_out_queue_get_count_prio(vsession->out_queue, prio) > 0) &&
			(sum_len < prio_win) &&
			(buffer_pos < vconn->io_ctx.mtu))
	{
		cmd_count = 0;
		cmd_share = 0;

		/* Compute how many commands could be compressed to the packet
		 * and compute right length of compressed commands. */
		cmd_len = ((prio_win - sum_len)<(vconn->io_ctx.mtu - buffer_pos)) ?
				(prio_win - sum_len) :
				(vconn->io_ctx.mtu - buffer_pos);

		/* Remove command from queue */
		cmd = v_out_queue_pop(vsession->out_queue, prio, &cmd_count, &cmd_share, &cmd_len);

		/* When it is not possible to pop more commands from queue, then break
		 * while loop */
		if(cmd == NULL) {
			break;
		}

		/* Is this command fake command? */
		if(cmd->id < MIN_CMD_ID) {
			if(cmd->id == FAKE_CMD_CONNECT_TERMINATE) {
				struct VS_CTX *vs_ctx = CTX_server_ctx(C);
				if(vs_ctx != NULL) {
					vconn->host_state = UDP_SERVER_STATE_CLOSEREQ;
				} else {
					vconn->host_state = UDP_CLIENT_STATE_CLOSING;
				}
			} else if(cmd->id == FAKE_CMD_FPS) {
				struct Fps_Cmd *fps_cmd = (struct Fps_Cmd*)cmd;
				/* Change value of FPS. It will be sent in negotiate command
				 * until it is confirmed be the peer (server) */
				vsession->fps_host = fps_cmd->fps;
			}
			v_cmd_destroy(&cmd);
		} else {

			/* What was size of command in queue */
			cmd_size = v_cmd_size(cmd);

			if(!(buffer_pos < (vconn->io_ctx.mtu - cmd_size))) {
				/* When there is not enough space for other command,
				 * then push command back to the beginning of queue. */
				v_out_queue_push_head(vsession->out_queue, prio, cmd);
				break;
			} else {

				/* Update total size of commands that were poped from queue */
				*tot_cmd_size += cmd_size;

				/* When compression is not allowed, then add this command as is */
				if( vconn->host_cmd_cmpr == CMPR_NONE) {
					cmd_count = 0;
					cmd_len = cmd_size;
					/* Debug print */
					v_print_log(VRS_PRINT_DEBUG_MSG, "Cmd: %d, count: %d, length: %d\n",
							cmd->id, cmd_count, cmd_len);
					/* Add command to the buffer */
					buffer_pos += v_cmd_pack(&io_ctx->buf[buffer_pos], cmd, cmd_len, 0);
				} else {
					/* When command compression is allowed and was ID of command changed? */
					if( (cmd->id != last_cmd_id) || (last_cmd_count <= 0) )	{
						/* When this command is alone, then use default command size */
						if(cmd_count == 0) {
							cmd_len = cmd_size;
						} else {
							/* FIXME: do not recompute command length here, but do it right,
							 * when command is added to the queue */
							cmd_len = v_cmds_len(cmd, cmd_count, cmd_share, cmd_len);
						}
						/* Debug print */
						v_print_log(VRS_PRINT_DEBUG_MSG, "Cmd: %d, count: %d, length: %d\n",
								cmd->id, cmd_count, cmd_len);
						/* Add command to the buffer */
						buffer_pos += v_cmd_pack(&io_ctx->buf[buffer_pos], cmd, cmd_len, cmd_share);
						/* Set up current count of commands in the line */
						last_cmd_count = cmd_count;
						/* Update summary of commands length */
						sum_len += cmd_len;
					} else {
						buffer_pos += v_cmd_pack(&io_ctx->buf[buffer_pos], cmd, 0, cmd_share);
					}
				}

				/* Print command */
				v_cmd_print(VRS_PRINT_DEBUG_MSG, cmd);

				/* TODO: remove command alias here (layer value set/unset) */

				/* Add command to the packet history */
				ret = v_packet_history_add_cmd(&vconn->packet_history, sent_packet, cmd, prio);
				assert(ret==1);

				/* Update last command id */
				last_cmd_id = cmd->id;
				/* Decrement counter of commands in queue */
				last_cmd_count--;
			}
		}
	}

	return buffer_pos;
}
Beispiel #21
0
/* Generic receiving and handling of packet */
int vc_receive_and_handle_packet(struct vContext *C, int handle_packet(struct vContext *C))
{
	struct VDgramConn *dgram_conn = CTX_current_dgram_conn(C);
	struct IO_CTX *io_ctx = CTX_io_ctx(C);
	struct VPacket *r_packet = CTX_r_packet(C);
	int ret, error_num;
	long int sec = 0, usec = 0;
	fd_set set;
	struct timeval tv;

	/* Initialize set */
	FD_ZERO(&set);
	FD_SET(dgram_conn->io_ctx.sockfd, &set);

	switch(dgram_conn->host_state) {
		case UDP_CLIENT_STATE_REQUEST:
		case UDP_CLIENT_STATE_PARTOPEN:
		case UDP_CLIENT_STATE_CLOSING:
			sec = INIT_TIMEOUT + (int) (v_exponential_backoff(dgram_conn->state[dgram_conn->host_state].attempts) * (rand() / (RAND_MAX + 1.0)));
			usec = 0;
			break;
		case UDP_CLIENT_STATE_OPEN:
			sec = 0;
			usec = 10000;
			break;
	}

	/* Initialize time value */
	tv.tv_sec = sec;
	tv.tv_usec = usec;

	/* Wait on response from server */
	if( (ret = select(io_ctx->sockfd+1, &set, NULL, NULL, &tv)) == -1 ) {
		if(is_log_level(VRS_PRINT_ERROR)) v_print_log(VRS_PRINT_ERROR, "%s:%s():%d select(): %s\n", __FILE__, __FUNCTION__,  __LINE__, strerror(errno));
		return RECEIVE_PACKET_ERROR;
	}
	/* Check if the event occurred on sockfd */
	else if(ret>0 && FD_ISSET(io_ctx->sockfd, &set)) {
		/* Try to receive packet from server */
		if(v_receive_packet(io_ctx, &error_num) == -1) {
			switch(error_num) {
				case ECONNREFUSED:	/* A remote host refused this connection */
					return RECEIVE_PACKET_ERROR;
				case EAGAIN:
				case EBADF:
				case EFAULT:
				case EINTR:
				case EINVAL:
				case ENOMEM:
				case ENOTCONN:
				case ENOTSOCK:
					break;
			}
		/* Address of received packet has to be same as address of server, when
		 * connection is not connected */
		} else if((io_ctx->flags & SOCKET_CONNECTED) ||
				v_compare_addr_and_port(&dgram_conn->peer_address, &dgram_conn->io_ctx.peer_addr))
		{
			/* The size of buffer must be bigger then zero */
			if(dgram_conn->io_ctx.buf_size>0) {
				int buffer_pos = 0;

				/* Get time of receiving packet */
				gettimeofday(&tv, NULL);

				/* Extract verse header from packet */
				ret = v_unpack_packet_header(io_ctx->buf,
						io_ctx->buf_size,
						r_packet);
				if(ret != VERSE_PACKET_HEADER_SIZE)
					return RECEIVE_PACKET_CORRUPTED;

				/* Check for right packet header version number */
				if(r_packet->header.version != VRS_VERSION) {
					if(is_log_level(VRS_PRINT_WARNING)) v_print_log(VRS_PRINT_WARNING, "Packet with unsupported version: %d was dropped.\n",
							r_packet->header.version);
					return RECEIVE_PACKET_CORRUPTED;
				}

				/* Compute RWIN */
				dgram_conn->rwin_peer = r_packet->header.window << dgram_conn->rwin_peer_scale;

				/* Extract system commands from packet */
				buffer_pos += v_unpack_packet_system_commands(io_ctx->buf,
						io_ctx->buf_size,
						r_packet);

				/* Are there some node commands in the buffer? */
				if(io_ctx->buf_size > buffer_pos) {
					/* Set pointer to not proceeded buffer */
					r_packet->data = (uint8*)&io_ctx->buf[buffer_pos];
					/* Set size of not proceeded buffer */
					r_packet->data_size = io_ctx->buf_size - buffer_pos;
				} else {
					r_packet->data = NULL;
					r_packet->data_size = 0;
				}

				/* When important things are done, then print content of the
				 * command (in debug mode) */
				v_print_receive_packet(C);

				/* Handle received packet and its data */
				ret = handle_packet(C);

				if(ret == RECEIVE_PACKET_SUCCESS) {
					/* Update information about last received packet payload
					 * packet */
					if(r_packet->header.flags & PAY_FLAG) {
						dgram_conn->last_r_pay = r_packet->header.payload_id;
						/* Update time of last received payload packet */
						dgram_conn->tv_pay_recv.tv_sec = tv.tv_sec;
						dgram_conn->tv_pay_recv.tv_usec = tv.tv_usec;
					}
					/* Update information about last received packet acknowledgment
					 * packet */
					if(r_packet->header.flags & ACK_FLAG) {
						dgram_conn->last_r_ack = r_packet->header.ack_nak_id;
					}
				}

				return ret;

			} else {
				if(is_log_level(VRS_PRINT_WARNING)) v_print_log(VRS_PRINT_WARNING, "Packet with zero size was dropped.\n");
				return RECEIVE_PACKET_CORRUPTED;
			}
		} else {
			/* When some 'bad' packet is received on this port, then do not increase counter
			 * of connection attempts (packet was received from address, that does not belong
			 * to the server) */
			if(is_log_level(VRS_PRINT_WARNING))
				v_print_log(VRS_PRINT_WARNING, "Packet from unknown address was dropped.\n");

			return RECEIVE_PACKET_FAKED;	/* This should not happen, because connect() function is used */
		}
	}

	return RECEIVE_PACKET_TIMEOUT;
}
Beispiel #22
0
/**
 * \brief This function send packets in OPEN and CLOSEREQ state.
 */
int send_packet_in_OPEN_CLOSEREQ_state(struct vContext *C)
{
	struct VDgramConn *vconn = CTX_current_dgram_conn(C);
	struct IO_CTX *io_ctx = CTX_io_ctx(C);
	struct VSession *vsession = CTX_current_session(C);
	struct VPacket *s_packet = CTX_s_packet(C);
	struct VSent_Packet *sent_packet = NULL;
	unsigned short buffer_pos = 0;
	struct timeval tv;
	int ret, keep_alive_packet = -1, full_packet = 0;
	int error_num;
	uint16 swin, prio_win, sent_size = 0;
	uint32 rwin;
	int cmd_rank = 0;

	/* Verse packet header */
	s_packet->header.version = 1;

	/* Clear header flags */
	s_packet->header.flags = 0;

	/* Check if it is necessary to send payload packet */
	ret = check_pay_flag(C);
	if(ret!=0) {
		s_packet->header.flags |= PAY_FLAG;
		if(ret==2) {
			keep_alive_packet = 1;
		}
	}

	/* When server is in CLOSEREQ state, then FIN flag should be set up */
	if(vconn->host_state == UDP_SERVER_STATE_CLOSEREQ) {
		s_packet->header.flags |= FIN_FLAG;
	}

	/* Check if it is necessary to send acknowledgment of received payload
	 * packet */
	ret = check_ack_nak_flag(C);
	if(ret==1) {
		s_packet->header.flags |= ACK_FLAG;

		/* Update last acknowledged Payload packet */
		vconn->last_acked_pay = vconn->last_r_pay;

		/* Add ACK and NAK commands from the list of ACK and NAK commands to the
		 * packet (only max count of ACK and NAK commands could be added to
		 * the packet) */
		for(cmd_rank = 0;
				cmd_rank < vconn->ack_nak.count &&
				cmd_rank < MAX_SYSTEM_COMMAND_COUNT;
				cmd_rank++)
		{
			s_packet->sys_cmd[cmd_rank].ack_cmd.id = vconn->ack_nak.cmds[cmd_rank].id;
			s_packet->sys_cmd[cmd_rank].ack_cmd.pay_id = vconn->ack_nak.cmds[cmd_rank].pay_id;
		}
		s_packet->sys_cmd[cmd_rank].cmd.id = CMD_RESERVED_ID;

	}

	/* If there is no need to send Payload or AckNak packet, then cancel
	 * sending of packet */
	if(! ((s_packet->header.flags & PAY_FLAG) ||
			(s_packet->header.flags & ACK_FLAG)) ) return SEND_PACKET_CANCELED;

	s_packet->header.flags |= ANK_FLAG;
	s_packet->header.ank_id = vconn->ank_id;

	/* Compute current windows for flow control and congestion control */
	set_host_rwin(C);
	set_host_cwin(C);

	/* Set window of flow control that will sent to receiver */
	rwin = vconn->rwin_host >> vconn->rwin_host_scale;
	s_packet->header.window = (unsigned short)(rwin > 0xFFFF) ? 0xFFFF : rwin;
	/*printf("\t---real window: %d---\n", s_packet->header.window);*/

	/* Compute how many data could be sent to not congest receiver */
	rwin = vconn->rwin_peer - vconn->sent_size;

	/* Select smallest window for sending (congestion control window or flow control window)*/
	swin = (vconn->cwin < rwin) ? vconn->cwin : rwin;

	/* Set up Payload ID, when there is need to send payload packet */
	if(s_packet->header.flags & PAY_FLAG)
		s_packet->header.payload_id = vconn->host_id + vconn->count_s_pay;
	else
		s_packet->header.payload_id  = 0;

	/* Set up AckNak ID, when there are some ACK or NAK command in the packet */
	if(s_packet->header.flags & ACK_FLAG)
		s_packet->header.ack_nak_id = vconn->count_s_ack;
	else
		s_packet->header.ack_nak_id = 0;

	/* When negotiated and used FPS is different, then pack negotiate command
	 * for FPS */
	if(vsession->fps_host != vsession->fps_peer) {
		cmd_rank += v_add_negotiate_cmd(s_packet->sys_cmd, cmd_rank,
				CMD_CHANGE_L_ID, FTR_FPS, &vsession->fps_host, NULL);
	} else {
		if(vsession->tmp_flags & SYS_CMD_NEGOTIATE_FPS) {
			cmd_rank += v_add_negotiate_cmd(s_packet->sys_cmd, cmd_rank,
					CMD_CONFIRM_L_ID, FTR_FPS, &vsession->fps_peer, NULL);
			/* Send confirmation only once for received system command */
			vsession->tmp_flags &= ~SYS_CMD_NEGOTIATE_FPS;
		}
	}

	v_print_send_packet(C);

	/* Fill buffer */
	buffer_pos += v_pack_packet_header(s_packet, &io_ctx->buf[buffer_pos]);
	buffer_pos += v_pack_dgram_system_commands(s_packet, &io_ctx->buf[buffer_pos]);

	/* When this is not pure keep alive packet */
	if(s_packet->header.flags & PAY_FLAG) {

		sent_packet = v_packet_history_add_packet(&vconn->packet_history, s_packet->header.payload_id);

		assert(sent_packet != NULL);

		if(keep_alive_packet != 1) {
			real32 prio_sum_high, prio_sum_low, r_prio;
			uint32 prio_count;
			int16 prio, max_prio, min_prio;
			uint16 tot_cmd_size;

			/* Print outgoing command with green color */
			if(is_log_level(VRS_PRINT_DEBUG_MSG)) {
				printf("%c[%d;%dm", 27, 1, 32);
			}

			max_prio = v_out_queue_get_max_prio(vsession->out_queue);
			min_prio = v_out_queue_get_min_prio(vsession->out_queue);

			prio_sum_high = v_out_queue_get_prio_sum_high(vsession->out_queue);
			prio_sum_low = v_out_queue_get_prio_sum_low(vsession->out_queue);

			v_print_log(VRS_PRINT_DEBUG_MSG, "Packing prio queues, cmd count: %d\n", v_out_queue_get_count(vsession->out_queue));

			/* Go through all priorities and pick commands from priority queues */
			for(prio = max_prio; prio >= min_prio; prio--)
			{
				/* TODO: Add better check here */
				if(prio <= VRS_DEFAULT_PRIORITY && buffer_pos >= vconn->io_ctx.mtu) {
					break;
				}

				prio_count = v_out_queue_get_count_prio(vsession->out_queue, prio);

				if(prio_count > 0) {
					r_prio = v_out_queue_get_prio(vsession->out_queue, prio);

					/* Compute size of buffer that could be occupied by
					 * commands from this queue */
					if(prio >= VRS_DEFAULT_PRIORITY) {
						prio_win = ((swin - buffer_pos)*r_prio)/prio_sum_high;
					} else {
						prio_win = ((swin - buffer_pos)*r_prio)/prio_sum_low;
					}

					/* Debug print */
					v_print_log(VRS_PRINT_DEBUG_MSG, "Queue: %d, count: %d, r_prio: %6.3f, prio_win: %d\n",
							prio, prio_count, r_prio, prio_win);

					/* Get total size of commands that were stored in queue (sent_size) */
					tot_cmd_size = 0;
					/* Pack commands from queues with high priority to the buffer */
					buffer_pos = pack_prio_queue(C, sent_packet, buffer_pos, prio, prio_win, &tot_cmd_size);
					sent_size += tot_cmd_size;
				}
			}

			/* Use default color for output */
			if(is_log_level(VRS_PRINT_DEBUG_MSG)) {
				printf("%c[%dm", 27, 0);
			}
		} else {
			if(is_log_level(VRS_PRINT_DEBUG_MSG)) {
				printf("%c[%d;%dm", 27, 1, 32);
				v_print_log(VRS_PRINT_DEBUG_MSG, "Keep alive packet\n");
				printf("%c[%dm", 27, 0);
			}
		}
	}

	/* Update sent_size */
	vconn->sent_size += sent_size;

	io_ctx->buf_size = buffer_pos;

	/* Send buffer */
	ret = v_send_packet(io_ctx, &error_num);

	if(ret==SEND_PACKET_SUCCESS) {
		gettimeofday(&tv, NULL);

		/* Update time of sending last payload packet */
		if(s_packet->header.flags & PAY_FLAG) {
			vconn->tv_pay_send.tv_sec = tv.tv_sec;
			vconn->tv_pay_send.tv_usec = tv.tv_usec;
			/* Store time of sending packet in history of sent packets. It is
			 * used for computing RTT and SRTT */
			if(sent_packet != NULL) {
				sent_packet->tv.tv_sec = tv.tv_sec;
				sent_packet->tv.tv_usec = tv.tv_usec;
			}
		}

		/* Update time of sending last acknowledgment packet */
		if(s_packet->header.flags & ACK_FLAG) {
			vconn->tv_ack_send.tv_sec = tv.tv_sec;
			vconn->tv_ack_send.tv_usec = tv.tv_usec;
		}

		/* Update counter of sent packets */
		if(s_packet->header.flags & PAY_FLAG) vconn->count_s_pay++;
		if(s_packet->header.flags & ACK_FLAG) vconn->count_s_ack++;

		/* If the packet was sent full and there are some pending data to send
		 * then modify returned value*/
		if(full_packet == 1) {
			ret = SEND_PACKET_FULL;
		}
	} else {
		/* When packet wasn't sent, then remove this packet from history */
		if(sent_packet != NULL) {
			v_packet_history_rem_packet(C, s_packet->header.payload_id);
		}
	}

	/*v_print_packet_history(&vconn->packet_history);*/

	return ret;
}
Beispiel #23
0
/**
 * \brief This function is called, when acknowledgment packet was received.
 *
 * This function processes all ACK and NAK commands and it add not obsolete
 * commands from the history of sent packets to the out queue again. This
 * function also removes positively and negatively acknowledged packets from
 * history of sent packets. ANK ID is updated.
 *
 * \param[in] *C	The verse context.
 *
 * \return	This function returns index of last ACK command in sequence of
 * system command, when ACK and NAK commands are the beginning of system
 * commands, otherwise it returns -2. When no ACK or NAK command was found,
 * then -1 is returned;
 */
static int handle_ack_nak_commands(struct vContext *C)
{
	struct VSession *vsession = CTX_current_session(C);
	struct VDgramConn *vconn = CTX_current_dgram_conn(C);
	struct VPacket *r_packet = CTX_r_packet(C);
	struct VSent_Packet *sent_packet;
	struct VSent_Command *sent_cmd, *sent_cmd_prev;
	unsigned long int rtt = ULONG_MAX;
	struct timeval tv;
	uint32 ack_id, nak_id;
	int i, ret=-1;

	gettimeofday(&tv, NULL);

	/* Compute SRTT */
	if(r_packet->sys_cmd[0].cmd.id==CMD_ACK_ID) {
		unsigned long int tmp;
		int i=0;

		/* Try to find the smallest RTT from acknowledged packets */
		for(i=0; r_packet->sys_cmd[i].cmd.id!=CMD_RESERVED_ID; i++) {
			if(r_packet->sys_cmd[i].cmd.id==CMD_ACK_ID) {
				sent_packet = v_packet_history_find_packet(&vconn->packet_history,
					r_packet->sys_cmd[i].ack_cmd.pay_id);
				if(sent_packet!=NULL) {
					tmp = packet_rtt(sent_packet, &tv);
					if(tmp<rtt) rtt=tmp;
				}
			}
		}

		if(rtt<ULONG_MAX) {
			/* Computation of SRTT as described in RFC */
			if(vconn->srtt==0) {
				vconn->srtt = rtt;
			} else {
				vconn->srtt = RTT_ALPHA*vconn->srtt + (1-RTT_ALPHA)*rtt;
			}
			v_print_log(VRS_PRINT_DEBUG_MSG, "RTT: %d [us]\n", rtt);
			v_print_log(VRS_PRINT_DEBUG_MSG, "SRTT: %d [us]\n", vconn->srtt);
		}
	}


	/* Process all ACK and NAK commands. ACK and NAK commands should be first
	 * and there should not be other system commands between ACK and NAK
	 * commands. */
	for(i=0;
			r_packet->sys_cmd[i].cmd.id == CMD_NAK_ID ||
					r_packet->sys_cmd[i].cmd.id == CMD_ACK_ID;
			i++) {

		if(r_packet->sys_cmd[i].cmd.id == CMD_ACK_ID) {
			/* Check if ACK and NAK commands are the first system commands */
			if(ret!=-2 && ret==i-1) {
				ret = i;
			} else {
				ret = -2;
			}
			/* If this is not the last ACK command in the sequence of
			 * ACK/NAK commands, then remove all packets from history of
			 * sent packet, that are in following sub-sequence of ACK
			 * commands */
			if(r_packet->sys_cmd[i+1].cmd.id == CMD_NAK_ID ||
					r_packet->sys_cmd[i+1].cmd.id == CMD_ACK_ID)
			{
				/* Remove all acknowledged payload packets from the history
				 * of sent payload packets */
				for(ack_id = r_packet->sys_cmd[i].ack_cmd.pay_id;
						ack_id < r_packet->sys_cmd[i+1].nak_cmd.pay_id;
						ack_id++)
				{
					v_packet_history_rem_packet(C, ack_id);
				}
			} else {
				/* Remove this acknowledged payload packets from the history
				 * of sent payload packets */
				v_packet_history_rem_packet(C, r_packet->sys_cmd[i].ack_cmd.pay_id);
				/* This is the last ACK command in the sequence of ACK/NAK
				 * commands. Update ANK ID. */
				vconn->ank_id = r_packet->sys_cmd[i].ack_cmd.pay_id;
			}
		} else if(r_packet->sys_cmd[i].cmd.id == CMD_NAK_ID) {
			/* Check if ACK and NAK commands are the first system commands */
			if(ret!=-2 && ret==i-1) {
				ret = i;
			} else {
				ret = -2;
			}
			/* Go through the sub-sequence of NAk commands and try to re-send
			 * not-obsolete data from these packets */
			for(nak_id = r_packet->sys_cmd[i].nak_cmd.pay_id;
					nak_id < r_packet->sys_cmd[i+1].ack_cmd.pay_id;
					nak_id++)
			{
				/* Add not obsolete data of lost packet to the outgoing queue */
				/* Update ANK ID */
				sent_packet = v_packet_history_find_packet(&vconn->packet_history, nak_id);
				if(sent_packet != NULL) {
					v_print_log(VRS_PRINT_DEBUG_MSG, "Try to re-send packet: %d\n", nak_id);
					sent_cmd = sent_packet->cmds.last;

					/* Go through all commands in command list and add not
					 * obsolete commands to the outgoing queue */
					while(sent_cmd != NULL) {
						sent_cmd_prev = sent_cmd->prev;
						if(sent_cmd->vbucket != NULL && sent_cmd->vbucket->data != NULL) {

							/* Try to add command back to the outgoing command queue */
							if(v_out_queue_push_head(vsession->out_queue,
									sent_cmd->prio,
									(struct Generic_Cmd*)sent_cmd->vbucket->data) == 1)
							{
								/* Remove bucket from the history of sent commands too */
								v_hash_array_remove_item(&vconn->packet_history.cmd_hist[sent_cmd->id]->cmds, sent_cmd->vbucket->data);

								/* When command was added back to the queue,
								 * then delete only sent command */
								v_list_free_item(&sent_packet->cmds, sent_cmd);

							}
						}
						sent_cmd = sent_cmd_prev;
					}

					/* When all not obsolete commands are added to outgoing
					 * queue, then this packet could be removed from packet
					 * history*/
					v_packet_history_rem_packet(C, nak_id);
				}
			}
		}
	}

	return ret;
}
Beispiel #24
0
/**
 * \brief This function is the callback function for received Change_L
 * commands in REQUEST state.
 */
static int vc_REQUEST_CHANGE_L_cb(struct vContext *C, struct Generic_Cmd *cmd)
{
	struct VSession *vsession = CTX_current_session(C);
	struct VDgramConn *dgram_conn = CTX_current_dgram_conn(C);
	struct Negotiate_Cmd *change_l_cmd = (struct Negotiate_Cmd*)cmd;
	int value_rank;

	if(change_l_cmd->feature == FTR_TOKEN) {
		/* This block of code checks, if received token is the same as the
		 * negotiated token. When token is not the same, then the client
		 * will not response to the received packet. */
		if( vsession->peer_token.str != NULL &&
				change_l_cmd->count > 0)
		{
			v_print_log(VRS_PRINT_DEBUG_MSG,
					"Remote TOKEN: %s proposed\n",
					change_l_cmd->value[0].string8.str);
			return 1;
		} else {
			v_print_log(VRS_PRINT_WARNING, "Peer proposed wrong TOKEN\n");
			return 0;
		}
	}

	/* Server proposes it's own Flow Control */
	if(change_l_cmd->feature == FTR_FC_ID) {
		int ret = 0;

		for(value_rank=0; value_rank<change_l_cmd->count; value_rank++) {
			/* Is value in "list" of supported methods */
			if(change_l_cmd->value[value_rank].uint8 == FC_NONE ||
					change_l_cmd->value[value_rank].uint8 == FC_TCP_LIKE)
			{
				/* It will try to use first found supported method, but ... */
				if(dgram_conn->fc_meth == FC_RESERVED) {
					/* Flow Control has not been proposed yet */
				} else {
					/* Server has to propose same FC methods for client
					 * and server. Client can't use different method then
					 * server and vice versa. */
					if(dgram_conn->fc_meth != change_l_cmd->value[value_rank].uint8) {
						v_print_log(VRS_PRINT_WARNING,
								"Skipping proposed local FC :%d; it is not teh same as proposed remote FC: %d\n",
								change_l_cmd->value[value_rank].uint8,
								dgram_conn->fc_meth);
						continue;
					}
				}
				dgram_conn->fc_meth = change_l_cmd->value[value_rank].uint8;
				v_print_log(VRS_PRINT_DEBUG_MSG,
						"Local Flow Control ID: %d proposed\n",
						change_l_cmd->value[0].uint8);
				ret = 1;
				break;
			} else {
				v_print_log(VRS_PRINT_ERROR,
						"Skipping unsupported Flow Control method: %d\n",
						change_l_cmd->value[value_rank].uint8);
				continue;
			}
		}

		return ret;
	}

	/* Server proposes it's own scale of Flow Control Window */
	if(change_l_cmd->feature == FTR_RWIN_SCALE) {
		if(change_l_cmd->count >= 1) {
			dgram_conn->rwin_peer_scale = change_l_cmd->value[0].uint8;
			v_print_log(VRS_PRINT_DEBUG_MSG,
					"Scale of peer RWIN: %d proposed\n",
					dgram_conn->rwin_peer_scale);
			return 1;
		} else {
			v_print_log(VRS_PRINT_ERROR,
					"At last on value of RWIN scale has to be proposed\n");
			return 0;
		}
	}

	/* Ignore unknown feature */
	return 1;
}
Beispiel #25
0
int vc_OPEN_loop(struct vContext *C)
{
	struct VDgramConn *dgram_conn = CTX_current_dgram_conn(C);
	struct VPacket *r_packet = CTX_r_packet(C);
	struct Ack_Nak_Cmd ack_cmd;
	int ret;

	/* OPEN state */
	if(is_log_level(VRS_PRINT_DEBUG_MSG)) {
		printf("%c[%d;%dm", 27, 1, 31);
		v_print_log(VRS_PRINT_DEBUG_MSG, "Client state: OPEN\n");
		printf("%c[%dm", 27, 0);
	}

	/* Add ACK command to the list of ACK NAK commands to be send to the peer */
	ack_cmd.id = CMD_ACK_ID;
	ack_cmd.pay_id = r_packet->header.payload_id;
	v_ack_nak_history_add_cmd(&dgram_conn->ack_nak, &ack_cmd);

	/* Receiving of last packet in previous packet had to be successful, because
	 * client is in this state now :-). */
	ret = RECEIVE_PACKET_SUCCESS;

	while(dgram_conn->host_state == UDP_CLIENT_STATE_OPEN) {

		dgram_conn->state[UDP_CLIENT_STATE_OPEN].attempts++;

		/* Send payload data or keep-alive packet */
		if( vc_OPEN_send_packet(C) == SEND_PACKET_ERROR ) {
			return STATE_EXIT_ERROR;
		}

		/* Try to receive packet and handle received packet */
		ret = vc_receive_and_handle_packet(C, vc_OPEN_handle_packet);
		if(ret == RECEIVE_PACKET_SUCCESS) {
			continue;
		} else if(ret == RECEIVE_PACKET_TIMEOUT) {
			struct timeval tv;
			gettimeofday(&tv, NULL);
			/* When no valid packet received from server for defined time, then consider this
			 * connection as dead and return error */
			if((tv.tv_sec - dgram_conn->tv_pay_recv.tv_sec) >= VRS_TIMEOUT) {
				return STATE_EXIT_ERROR;
			} else {
				continue;
			}
		} else if(ret == RECEIVE_PACKET_CORRUPTED) {
			continue;
		} else if(ret == RECEIVE_PACKET_FAKED) {
			return STATE_EXIT_ERROR;
		} else if(ret == RECEIVE_PACKET_ERROR) {
			return STATE_EXIT_ERROR;
		}

#ifdef WITH_OPENSSL
		if(dgram_conn->io_ctx.flags & SOCKET_SECURED) {
			/* Did server close DTLS connection? */
			if((SSL_get_shutdown(dgram_conn->io_ctx.ssl) & SSL_RECEIVED_SHUTDOWN)) {
				return STATE_EXIT_ERROR;
			}
		}
#endif
	}

	return STATE_EXIT_SUCCESS;
}
Beispiel #26
0
/**
 * \brief This command handle received packets in OPEN state at client and
 * server in the same way.
 */
int handle_packet_in_OPEN_state(struct vContext *C)
{
	struct VDgramConn *vconn = CTX_current_dgram_conn(C);
	struct VSession *vsession = CTX_current_session(C);
	struct VPacket *r_packet = CTX_r_packet(C);
	int ret, first_sys_index, i;

	/* Does packet contains node commands? */
	if(r_packet->header.flags & PAY_FLAG) {
		ret = handle_node_commands(C);
		if(ret == RECEIVE_PACKET_UNORDERED)
			return ret;
		r_packet->acked=0;
	}

	/* Compute real RWIN of Flow Control */
	vconn->rwin_peer = r_packet->header.window << vconn->rwin_peer_scale;

	/* Was packet received with any ACK or NAK command? */
	if(r_packet->header.flags & ACK_FLAG) {
		ret = handle_ack_nak_commands(C);
	}

	/* Handle other system commands */
	if(ret>=0) {
		first_sys_index = ret;
	} else {
		first_sys_index = 0;
	}

	for(i=first_sys_index;
			i<MAX_SYSTEM_COMMAND_COUNT && r_packet->sys_cmd[i].cmd.id != CMD_RESERVED_ID;
			i++)
	{
		if(r_packet->sys_cmd[i].cmd.id == CMD_CHANGE_L_ID &&
				r_packet->sys_cmd[i].negotiate_cmd.feature == FTR_FPS &&
				r_packet->sys_cmd[i].negotiate_cmd.count > 0)
		{
			vsession->fps_host = vsession->fps_peer = r_packet->sys_cmd[i].negotiate_cmd.value->real32;
			vsession->tmp_flags |= SYS_CMD_NEGOTIATE_FPS;
		}

		if(r_packet->sys_cmd[i].cmd.id == CMD_CONFIRM_L_ID &&
				r_packet->sys_cmd[i].negotiate_cmd.feature == FTR_FPS &&
				r_packet->sys_cmd[i].negotiate_cmd.count > 0)
		{
			vsession->fps_peer = r_packet->sys_cmd[i].negotiate_cmd.value->real32;
		}
	}

	/* Was packet received with valid ANK ID? */
	if(r_packet->header.flags & ANK_FLAG) {
		/* Remove appropriate ACK and NAK commands form the history of ACK
		 * and NAK commands */
		v_ack_nak_history_remove_cmds(&vconn->ack_nak, r_packet->header.ank_id);
	}

	/* Does peer want to finish this connection? */
	if(r_packet->header.flags & FIN_FLAG) {
		/* Change host state. Main connection loop will do the rest. */
		vconn->host_state = UDP_CLIENT_STATE_CLOSING;
	}

	return RECEIVE_PACKET_SUCCESS;
}
Beispiel #27
0
int vc_create_dtls_connection(struct vContext *C)
{
	struct VC_CTX *vc_ctx = CTX_client_ctx(C);
	struct VSession *vsession = CTX_current_session(C);
	struct VDgramConn *dgram_conn = CTX_current_dgram_conn(C);
	struct timeval timeout;
	int ret = 0;

	v_print_log(VRS_PRINT_DEBUG_MSG, "Try to do DTLS handshake at UDP socket: %d\n",
			dgram_conn->io_ctx.sockfd);

	/* Create ssl for new connection */
	if( (dgram_conn->io_ctx.ssl = SSL_new(vc_ctx->dtls_ctx)) == NULL) {
		v_print_log(VRS_PRINT_ERROR, "SSL_new(%p)\n", (void*)vc_ctx->dtls_ctx);
		return 0;
	}

	/* Set state of bio as connected */
	if(dgram_conn->io_ctx.peer_addr.ip_ver == IPV4) {
		ret = BIO_ctrl(dgram_conn->io_ctx.bio, BIO_CTRL_DGRAM_SET_CONNECTED, 0, &dgram_conn->io_ctx.peer_addr.addr.ipv6);
	} else if(dgram_conn->io_ctx.peer_addr.ip_ver == IPV6) {
		ret = BIO_ctrl(dgram_conn->io_ctx.bio, BIO_CTRL_DGRAM_SET_CONNECTED, 0, &dgram_conn->io_ctx.peer_addr.addr.ipv4);
	}

	/* When BIO_ctrl was called with bad arguments, then it returns 0 */
	if(ret==0) {
		v_print_log(VRS_PRINT_ERROR, "BIO_ctrl()\n");
		SSL_free(dgram_conn->io_ctx.ssl);
		return 0;
	}

	/* Set and activate timeouts */
	timeout.tv_sec = 1;
	timeout.tv_usec = 0;
	BIO_ctrl(dgram_conn->io_ctx.bio, BIO_CTRL_DGRAM_SET_RECV_TIMEOUT, 0, &timeout);
	BIO_ctrl(dgram_conn->io_ctx.bio, BIO_CTRL_DGRAM_SET_SEND_TIMEOUT, 0, &timeout);

	/* Bind ssl and bio */
	SSL_set_bio(dgram_conn->io_ctx.ssl, dgram_conn->io_ctx.bio, dgram_conn->io_ctx.bio);

	/* Try to do DTLS handshake */
again:
	if ((ret = SSL_connect(dgram_conn->io_ctx.ssl)) <= 0) {
		int err = SSL_get_error(dgram_conn->io_ctx.ssl, ret);
		if(err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) {
			gettimeofday(&timeout, NULL);
			if((timeout.tv_sec - vsession->peer_token.tv.tv_sec) > VRS_TIMEOUT) {
				v_print_log(VRS_PRINT_ERROR, "Token timed out\n");
				return 0;
			}
			usleep(1000);
			goto again;
		}
		ERR_print_errors_fp(stderr);
		v_print_log(VRS_PRINT_ERROR, "SSL_connect() failed: %d -> %d\n", ret, err);
		SSL_free(dgram_conn->io_ctx.ssl);
		dgram_conn->io_ctx.ssl = NULL;
		dgram_conn->io_ctx.bio = NULL;
		return 0;
	} else {
		v_print_log(VRS_PRINT_DEBUG_MSG, "DTLS handshake finished\n");

		v_print_log(VRS_PRINT_DEBUG_MSG, "Current cipher: %s\n",
				SSL_CIPHER_get_name(SSL_get_current_cipher(dgram_conn->io_ctx.ssl)));
	}

	return 1;
}
Beispiel #28
0
int vc_REQUEST_loop(struct vContext *C)
{
	struct VC_CTX *vc_ctx = CTX_client_ctx(C);
	struct VDgramConn *dgram_conn = CTX_current_dgram_conn(C);
	int ret;

	/* REQUEST STATE */
	if(is_log_level(VRS_PRINT_DEBUG_MSG)) {
		printf("%c[%d;%dm", 27, 1, 31);
		v_print_log(VRS_PRINT_DEBUG_MSG, "Client state: REQUEST\n");
		printf("%c[%dm", 27, 0);
	}

	/* State of client */
	dgram_conn->host_state = UDP_CLIENT_STATE_REQUEST;

	/* Initialize callback functions for this state */
	dgram_conn->state[UDP_CLIENT_STATE_REQUEST].CHANGE_L_cb = vc_REQUEST_CHANGE_L_cb;
	dgram_conn->state[UDP_CLIENT_STATE_REQUEST].CHANGE_R_cb = vc_REQUEST_CHANGE_R_cb;
	dgram_conn->state[UDP_CLIENT_STATE_REQUEST].CONFIRM_L_cb = vc_REQUEST_CONFIRM_L_cb;
	dgram_conn->state[UDP_CLIENT_STATE_REQUEST].CONFIRM_R_cb = vc_REQUEST_CONFIRM_R_cb;

	dgram_conn->state[UDP_CLIENT_STATE_REQUEST].attempts = 0;

	while(dgram_conn->state[UDP_CLIENT_STATE_REQUEST].attempts < vc_ctx->max_connection_attempts) {
		/* Try to send a packet with connect request */
		if( vc_REQUEST_send_packet(C) == SEND_PACKET_ERROR) {
			return STATE_EXIT_ERROR;
		}
		/* Try to receive packet and handle received packet (update client state) */
		ret = vc_receive_and_handle_packet(C, vc_REQUEST_handle_packet);
		if(ret == RECEIVE_PACKET_SUCCESS) {
			break;			/* Break loop and receive to the next state */
		} else if(ret == RECEIVE_PACKET_TIMEOUT) {
			dgram_conn->state[UDP_CLIENT_STATE_REQUEST].attempts++;		/* No packet receive ... try it again */
		} else if(ret == RECEIVE_PACKET_CORRUPTED) {
			dgram_conn->state[UDP_CLIENT_STATE_REQUEST].attempts++;		/* Corrupted packet received ... try it again */
		} else if(ret == RECEIVE_PACKET_FAKED) {
			continue;		/* Packet wasn't received from the server (should not happen, because connect()) */
		} else if(ret == RECEIVE_PACKET_ERROR) {
			return STATE_EXIT_ERROR;
		}

#ifdef WITH_OPENSSL
		if(dgram_conn->io_ctx.flags & SOCKET_SECURED) {
			/* Did server close DTLS connection? */
			if((SSL_get_shutdown(dgram_conn->io_ctx.ssl) & SSL_RECEIVED_SHUTDOWN)) {
				return STATE_EXIT_ERROR;
			}
		}
#endif
	}

	/* Maximal count of connection attempts reached and the client is
	 * still in REQUEST state -> end handshake. */
	if(dgram_conn->host_state == UDP_CLIENT_STATE_REQUEST) {
		if(is_log_level(VRS_PRINT_ERROR)) v_print_log(VRS_PRINT_ERROR,
				"Maximum count of connection attempts reached %d.\n", vc_ctx->max_connection_attempts);
		return STATE_EXIT_ERROR;
	}

	return STATE_EXIT_SUCCESS;
}
Beispiel #29
0
/**
 * \brief This function is the callback function for received Confirm_L
 * commands in REQUEST state.
 */
static int vc_REQUEST_CONFIRM_L_cb(struct vContext *C, struct Generic_Cmd *cmd)
{
	struct VC_CTX *vc_ctx = CTX_client_ctx(C);
	struct VSession *vsession = CTX_current_session(C);
	struct VDgramConn *dgram_conn = CTX_current_dgram_conn(C);
	struct Negotiate_Cmd *confirm_l_cmd = (struct Negotiate_Cmd*)cmd;

	if(confirm_l_cmd->feature == FTR_TOKEN) {
		/* This block of code checks if the server confirmed send token. */
		if( vsession->host_token.str != NULL &&
				confirm_l_cmd->count == 1 &&
				strcmp((char*)confirm_l_cmd->value[0].string8.str, vsession->host_token.str)==0 )
		{
			v_print_log(VRS_PRINT_DEBUG_MSG,
					"Local TOKEN: %s confirmed\n",
					confirm_l_cmd->value[0].string8.str);
			return 1;
		} else {
			v_print_log(VRS_PRINT_WARNING,
					"COOCKIE: %s not confirmed\n",
					vsession->peer_token.str);
			return 0;
		}
	}

	/* Server should confirm client proposal of Congestion Control (local) */
	if(confirm_l_cmd->feature == FTR_CC_ID) {
		/* TODO: better implementation */
		if(confirm_l_cmd->count == 1 &&	/* Any confirm command has to include only one value */
				confirm_l_cmd->value[0].uint8 == CC_NONE) {	/* list of supported methods */
			v_print_log(VRS_PRINT_DEBUG_MSG,
					"Local Congestion Control ID: %d confirmed\n",
					confirm_l_cmd->value[0].uint8);
			dgram_conn->cc_meth = CC_NONE;
			return 1;
		} else {
			v_print_log(VRS_PRINT_ERROR, "Unsupported Congestion Control\n");
			return 0;
		}
	}

	/* Server should confirm client proposal of Flow Control Window scale (local) */
	if(confirm_l_cmd->feature == FTR_RWIN_SCALE) {
		if(confirm_l_cmd->count == 1 ) {
			if(vc_ctx->rwin_scale == confirm_l_cmd->value[0].uint8) {
				v_print_log(VRS_PRINT_DEBUG_MSG,
						"Scale of host RWIN: %d confirmed\n", vc_ctx->rwin_scale);
				dgram_conn->rwin_host_scale = vc_ctx->rwin_scale;
				return 1;
			} else {
				v_print_log(VRS_PRINT_ERROR, "Scale of RWIN: %d != %d wasn't confirmed\n",
						vc_ctx->rwin_scale,
						confirm_l_cmd->value[0].uint8);
				dgram_conn->rwin_host_scale = 0;
				return 0;
			}
		} else {
			v_print_log(VRS_PRINT_ERROR, "One value of RWIN scale should to be confirmed\n");
			return 0;
		}
	}

	/* Server should confirm client proposal of command compression */
	if(confirm_l_cmd->feature == FTR_CMD_COMPRESS) {
		if(confirm_l_cmd->count == 1) {
			if(confirm_l_cmd->value[0].uint8 == CMPR_NONE ||
					confirm_l_cmd->value[0].uint8 == CMPR_ADDR_SHARE)
			{
				v_print_log(VRS_PRINT_DEBUG_MSG, "Local Command Compression: %d confirmed\n",
						confirm_l_cmd->value[0].uint8);
				dgram_conn->host_cmd_cmpr = confirm_l_cmd->value[0].uint8;
				return 1;
			} else {
				v_print_log(VRS_PRINT_ERROR, "Unsupported Command Compress\n");
				return 0;
			}
		}
	}

	/* Ignore unknown feature */
	return 1;
}