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; }
/** * \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; }
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; }
/** * \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; }
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; }
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; }
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); } }
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; }
/** * \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; }
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; }
/** * \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; }
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); } }
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; }
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); } }
/** * \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; } }
/** * \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; }
/** * \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; }
/** * \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; } }
/** * \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; }
/** * \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; }
/* 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; }
/** * \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; }
/** * \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; }
/** * \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; }
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; }
/** * \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; }
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; }
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; }
/** * \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; }