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; }
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_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; }
/** * \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; }
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 v_print_receive_packet(struct vContext *C) { struct IO_CTX *io_ctx = CTX_io_ctx(C); struct VDgramConn *dgram_conn = CTX_current_dgram_conn(C); struct VPacket *r_packet = CTX_r_packet(C); if(is_log_level(VRS_PRINT_DEBUG_MSG)) { printf("%c[%d;%dm", 27, 1, 34); v_print_log(VRS_PRINT_DEBUG_MSG, "Receive packet: "); v_print_log_simple(VRS_PRINT_DEBUG_MSG, "Socket: %d, ", io_ctx->sockfd); v_print_log_simple(VRS_PRINT_DEBUG_MSG, "bufsize: %d, ", io_ctx->buf_size); v_print_addr_port(VRS_PRINT_DEBUG_MSG, &io_ctx->peer_addr); v_print_packet_header(VRS_PRINT_DEBUG_MSG, r_packet); v_print_log(VRS_PRINT_DEBUG_MSG, "Shift: %d -> Window: %d\n", dgram_conn->rwin_host_scale, (unsigned int)r_packet->header.window << dgram_conn->rwin_host_scale); v_print_packet_sys_cmds(VRS_PRINT_DEBUG_MSG, r_packet); printf("%c[%dm", 27, 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; }
/* 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; }
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; }
/** * \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 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; }