Date: Fri, 29 Jun 2007 15:15:06 GMT From: Alexey Tarasov <taleks@FreeBSD.org> To: Perforce Change Reviews <perforce@FreeBSD.org> Subject: PERFORCE change 122529 for review Message-ID: <200706291515.l5TFF6rT046413@repoman.freebsd.org>
next in thread | raw e-mail | index | archive | help
http://perforce.freebsd.org/chv.cgi?CH=122529 Change 122529 by taleks@taleks_th on 2007/06/29 15:14:52 Updated segment resending code, added tcp_resend_update() to recalc checksum with latest ack value. Updated pxe_tcp_send() to use segments functions. Changed await function to be able perform resending during waiting. Affected files ... .. //depot/projects/soc2007/taleks-pxe_http/pxe_connection.c#2 edit .. //depot/projects/soc2007/taleks-pxe_http/pxe_segment.c#2 edit .. //depot/projects/soc2007/taleks-pxe_http/pxe_segment.h#2 edit .. //depot/projects/soc2007/taleks-pxe_http/pxe_tcp.c#4 edit Differences ... ==== //depot/projects/soc2007/taleks-pxe_http/pxe_connection.c#2 (text+ko) ==== @@ -104,6 +104,8 @@ case PXE_AWAIT_STARTTRY: /* nothing to do */ case PXE_AWAIT_FINISHTRY: + pxe_resend_check(wait_data->connection); + break; case PXE_AWAIT_END: default: break; @@ -143,12 +145,14 @@ connection->next_recv = 0; /* NOTE: need to make more correct initial number */ - connection->iss = (filter->src_ip ^ filter->dst_ip) + (uint32_t)pxe_get_secs(); + connection->iss = (filter->src_ip + filter->dst_ip) + (uint32_t)pxe_get_secs(); connection->next_send = connection->iss; connection->filter = filter; connection->recv = &sock->recv_buffer; connection->send = &sock->send_buffer; + + pxe_resend_init(connection); if (!pxe_tcp_send(connection, 0, PXE_TCP_SYN)) { printf("pxe_tcp_connect(): failed to send SYN.\n"); @@ -170,7 +174,7 @@ * connection will fell in this state in pxe_tcp_callback(), * after receiving SYN ACK and sending ACK to remote host */ - if (!pxe_await(tcp_await, 1, PXE_TCP_MSL, &wait_data)) { /* failed to get SYN/ACK */ + if (!pxe_await(tcp_await, 5, PXE_TCP_MSL / 5, &wait_data)) { /* failed to get SYN/ACK */ free_connection(connection); return (0); } @@ -233,13 +237,15 @@ /* await TIME_WITE state. * connection will fell in this state in pxe_tcp_callback(), - * after receiving SYN ACK and sending ACK to remote host + * TODO: add waiting of TCP_CLOSED also */ - if (!pxe_await(tcp_await, 1, PXE_TCP_MSL, &wait_data)) { /* failed to get SYN/ACK */ + if (!pxe_await(tcp_await, 5, PXE_TCP_MSL / 5, &wait_data)) { /* failed to get to TIME_WAIT state */ free_connection(connection); return (0); } + pxe_resend_free(connection); + #ifdef PXE_DEBUG printf("pxe_tcp_disconnect(): connection closed.\n"); #endif ==== //depot/projects/soc2007/taleks-pxe_http/pxe_segment.c#2 (text+ko) ==== @@ -23,6 +23,10 @@ * But in case it'll be redone to be more effective or just using other segment allocation * algorithm - this function may be needed. */ + +#ifdef PXE_DEBUG + tcp_resend_stats(connection); +#endif } /* pxe_resend_init() - initialize buffer map for connection @@ -62,7 +66,7 @@ /* tcp_segment_alloc() - allocates from send buffer memory for packet, * including segment data, IP & TCP headers * in: - * connection - connection, from which send byffer segment is allocated + * connection - connection, from which send buffer segment is allocated * allocBig - 1 if need big segment, 0 otherwise * out: * NULL - failed to allocate memory chunk(s) for segment @@ -177,8 +181,18 @@ /* block is used exclusevely by one "big" packet */ if (buf_blocks[block_index] == PXE_TCP_BLOCK_EXCLUSIVE) { + + if (segment->status != PXE_SEGMENT_SENT) + continue; /* it was not ever sent yet */ + if (cur_time >= segment->resend_at) { /* time to resend */ +#ifdef PXE_DEBUG + printf("pxe_resend_check(): %d:%d resending...\n", segment->resend_at, cur_time); +#endif + tcp_update_segment(connection, segment); pxe_tcp_send_segment(connection, segment); + segment->trys += 1; + segment->resend_at += PXE_RESEND_TIME * segment->trys; } continue; } @@ -188,15 +202,17 @@ for ( ; chunk_index < PXE_TCP_CHUNK_COUNT; ++chunk_index) { - if (segment->status != PXE_SEGMENT_FREE) { + if (segment->status == PXE_SEGMENT_SENT) { if (cur_time >= segment->resend_at) { /* time to resend */ - +#ifdef PXE_DEBUG + printf("pxe_resend_check(): %d:%d resending...\n", segment->resend_at, cur_time); +#endif + tcp_update_segment(connection, segment); pxe_tcp_send_segment(connection, segment); - - /* resend a minute later, if failed */ - segment->resend_at += 60; - segment->trys += 1; + /* resend later, with more delay with every try */ + segment->trys += 1; + segment->resend_at += PXE_RESEND_TIME * segment->trys; } } @@ -233,7 +249,14 @@ /* block is used exclusevely by one "big" packet */ if (connection->buf_blocks[block_index] == PXE_TCP_BLOCK_EXCLUSIVE) { - if (connection->una > segment->seq) { /* segment was acked, release it */ + + if (segment->status != PXE_SEGMENT_SENT) + continue; /* it was not ever sent yet */ + + if (connection->una >= segment->seq) { /* segment was acked, release it */ +#ifdef PXE_DEBUG + printf("pxe_resend_update(): block %d acked.\n", block_index); +#endif tcp_segment_free(connection, block_index, segment); } continue; @@ -244,8 +267,12 @@ for ( ; chunk_index < PXE_TCP_CHUNK_COUNT; ++chunk_index) { - if (segment->status != PXE_SEGMENT_FREE) { - if (connection->una > segment->seq) { /* segment was acked */ + if (segment->status == PXE_SEGMENT_SENT) { + + if (connection->una >= segment->seq) { /* segment was acked */ +#ifdef PXE_DEBUG + printf("pxe_resend_update(): chunk %d@%d acked.\n", chunk_index, block_index); +#endif tcp_segment_free(connection, block_index, segment); } } @@ -269,13 +296,12 @@ tcp_packet->tcphdr.dst_port = le2be16(connection->dst_port); tcp_packet->tcphdr.checksum = 0; tcp_packet->tcphdr.sequence = le2be32(connection->next_send); - - tcp_packet->tcphdr.data_off = (uint8_t)((sizeof(PXE_TCP_HDR)/4) << 4); + tcp_packet->tcphdr.data_off = sizeof(PXE_TCP_HDR); if (add_options == 1) { /* reserving 8 bytes for options */ length += 8; - tcp_packet->tcphdr.data_off += 2; + tcp_packet->tcphdr.data_off += 8; /* pointing to options, leading tcp_header */ PXE_TCP_DEFAULT_OPTIONS *options = (PXE_TCP_DEFAULT_OPTIONS *)(tcp_packet + 1); @@ -286,7 +312,13 @@ options->mss = le2be16(PXE_TCP_MSS); } + tcp_packet->tcphdr.data_off = (tcp_packet->tcphdr.data_off / 4) << 4; tcp_packet->tcphdr.urgent = 0; + + segment->trys = 0; + segment->resend_at = 0; + segment->size = length; + segment->seq = connection->next_send; } void @@ -331,39 +363,95 @@ if (tcp_packet->tcphdr.checksum == 0) tcp_packet->tcphdr.checksum = 0xffff; + /* setting sequence number next to the segment last byte + * when connection->una become this value we must remove packet + * from resend queue. + */ + segment->seq += (length - 4 * (tcp_packet->tcphdr.data_off >> 4) + 1); + #ifdef PXE_DEBUG_HELL printf("tcp_finish_segment(): checksum 0x%4x for %d bytes\n", tcp_packet->tcphdr.checksum, length); #endif } +void +tcp_update_segment(PXE_TCP_CONNECTION *connection, PXE_TCP_QUEUED_SEGMENT *segment) +{ + PXE_TCP_PACKET *tcp_packet = (PXE_TCP_PACKET *)(segment + 1); + + uint16_t length = segment->size - sizeof(PXE_IP_HDR); + + tcp_packet->tcphdr.ack_next = le2be32(connection->next_recv); + + PXE_BUFFER *recv_buffer = connection->recv; + + /* set window size to free buffer space size, or to zero if recv_buffer == NULL */ + tcp_packet->tcphdr.window_size = (recv_buffer != NULL) ? le2be16(recv_buffer->bufleft) : 0; + tcp_packet->tcphdr.checksum = 0; + + PXE_IP4_PSEUDO_HDR pseudo_hdr; + + pseudo_hdr.src_ip = pxe_get_ip32(PXE_IP_MY); + pseudo_hdr.dst_ip = connection->dst_ip; + pseudo_hdr.zero = 0; + pseudo_hdr.proto = PXE_TCP_PROTOCOL; + pseudo_hdr.length = le2be16(length); + + /* adding pseudo header checksum to checksum of tcp header with data + * and make it complimentary + */ + uint16_t part1 = pxe_ip_checksum(&pseudo_hdr, sizeof(PXE_IP4_PSEUDO_HDR)); + uint16_t part2 = pxe_ip_checksum(&tcp_packet->tcphdr, length); + + uint32_t tmp_sum = ((uint32_t)part1) + ((uint32_t)part2); + + if (tmp_sum & 0xf0000) { /* need carry out */ + tmp_sum -= 0xffff; + } + + tcp_packet->tcphdr.checksum = ~((uint16_t)(tmp_sum & 0xffff)); + + /* special case */ + if (tcp_packet->tcphdr.checksum == 0) + tcp_packet->tcphdr.checksum = 0xffff; + +#ifdef PXE_DEBUG_HELL + printf("tcp_update_segment(): checksum 0x%4x for %d bytes\n", tcp_packet->tcphdr.checksum, length); +#endif +} + /* pxe_tcp_send_segment() - send data segment via TCP protocol * in: + * connection - connection to which segment belongs * segment - segment to send * out: * 0 - failed * 1 - success */ int -pxe_tcp_send_segment(PXE_TCP_QUEUED_SEGMENT *segment) +pxe_tcp_send_segment(PXE_TCP_CONNECTION *connection, PXE_TCP_QUEUED_SEGMENT *segment) { PXE_TCP_PACKET *tcp_packet = (PXE_TCP_PACKET *)(segment + 1); - if (!pxe_ip_send(tcp_packet, tcp_packet->iphdr.dst_ip, PXE_TCP_PROTOCOL, segment->size, 1)) { - printf("pxe_tcp_send_send(): failed to send tcp packet to 0x%x\n", tcp_packet->iphdr.dst_ip); + if (!pxe_ip_send(tcp_packet, connection->dst_ip, PXE_TCP_PROTOCOL, segment->size, 1)) { + printf("pxe_tcp_send_send(): failed to send tcp packet to 0x%x\n", connection->dst_ip); return (0); } + /* mark segment to be checked in resend and update calls*/ + segment->status = PXE_SEGMENT_SENT; + #ifdef PXE_DEBUG PXE_IPADDR from; PXE_IPADDR to; - from.ip = tcp_packet->iphdr.src_ip; - to.ip = tcp_packet->iphdr.dst_ip; + from.ip = pxe_get_ip32(PXE_IP_MY); + to.ip = connection->dst_ip; - printf("pxe_tcp_send_segment(): tcp packet from %d.%d.%d.%d:%d to %d.%d.%d.%d:%d\n seq %d", - from.octet[0], from.octet[1], from.octet[2], from.octet[3], tcp_packet->tcphdr.src_port, - to.octet[0], to.octet[1], to.octet[2], to.octet[3], tcp_packet->tcphdr.dst_port, - segment->seq + printf("pxe_tcp_send_segment(): tcp packet from %d.%d.%d.%d:%d to %d.%d.%d.%d:%d\n next seq %d", + from.octet[0], from.octet[1], from.octet[2], from.octet[3], connection->src_port, + to.octet[0], to.octet[1], to.octet[2], to.octet[3], connection->dst_port, + segment->seq - connection->iss ); uint8_t flags = tcp_packet->tcphdr.flags; @@ -378,16 +466,65 @@ printf(" rst,"); if (flags & PXE_TCP_ACK) - printf(" ack %d,", be2le32(tcp_packet->tcphdr.ack_next)); + printf(" ack %d,", connection->next_recv - connection->irs); if (flags & PXE_TCP_URG) printf(" urg,"); if (flags & PXE_TCP_URG) printf(" psh,"); - - printf(" %d bytes.\n", segment->size); + + uint16_t length = segment->size - sizeof(PXE_IP_HDR) - 4 * (tcp_packet->tcphdr.data_off >> 4); + + printf(" %d bytes.\n", length); #endif return (1); } + +void +tcp_resend_stats(PXE_TCP_CONNECTION *connection) +{ + int block_index = 0; + PXE_BUFFER *buffer = connection->send; + uint8_t *buf_blocks = connection->buf_blocks; + void *data = NULL; + PXE_TCP_QUEUED_SEGMENT *segment = NULL; + + printf("pxe_resend_stats(): stats for connection 0x%x\n", connection); + + for ( ; block_index < PXE_TCP_BLOCK_COUNT; ++block_index) { + + /* start of block */ + data = buffer->data + PXE_TCP_CHUNK_COUNT * block_index * connection->chunk_size; + segment = (PXE_TCP_QUEUED_SEGMENT *)data; + + if (buf_blocks[block_index] != PXE_TCP_BLOCK_FREE) { + + if (buf_blocks[block_index] != PXE_TCP_BLOCK_EXCLUSIVE) { + /* search free chunk in block */ + int chunk_index = 0; + + for ( ; chunk_index < PXE_TCP_CHUNK_COUNT; ++chunk_index) { + + if (segment->status != PXE_SEGMENT_FREE) { + + printf("\tchunk %d@%d awaiting %d ack.\n", + chunk_index, block_index, segment->seq - connection->iss + ); + } + + /* next chunk in block */ + data += connection->chunk_size; + segment = (PXE_TCP_QUEUED_SEGMENT *)data; + } + + } else { + + printf("pxe_resend_stats(): block %d awaiting %d ack.\n", + block_index, segment->seq - connection->iss + ); + } + } + } +} ==== //depot/projects/soc2007/taleks-pxe_http/pxe_segment.h#2 (text+ko) ==== @@ -11,6 +11,8 @@ #define PXE_SEGMENT_USED 0x01 /* segment is filled with data, sent but not ACKed yet */ #define PXE_SEGMENT_SENT 0x02 +/* default resend time if not acked in seconds */ +#define PXE_RESEND_TIME 5 /* how much blocks in buffer */ #define PXE_TCP_BLOCK_COUNT 8 @@ -58,4 +60,25 @@ /* inits buffer map of connection */ void pxe_resend_init(PXE_TCP_CONNECTION *connection); +/* sends chhosed segment to adrressee */ +int pxe_tcp_send_segment(PXE_TCP_CONNECTION *connection, PXE_TCP_QUEUED_SEGMENT *segment); + +/* allocates in buffer space segment */ +PXE_TCP_QUEUED_SEGMENT * tcp_segment_alloc(PXE_TCP_CONNECTION *connection, int allocBig); + +/* releases memory used by segment */ +void tcp_segment_free(PXE_TCP_CONNECTION *connection, int block_index, PXE_TCP_QUEUED_SEGMENT *segment); + +/* fills most of fields of tcp header of segment */ +void tcp_start_segment(PXE_TCP_CONNECTION *connection, PXE_TCP_QUEUED_SEGMENT *segment, int add_options); + +/* finishes filling of tcp header, adds checksum */ +void tcp_finish_segment(PXE_TCP_CONNECTION *connection, PXE_TCP_QUEUED_SEGMENT *segment, uint8_t tcp_flags); + +/* when resending updates ack and checksum */ +void tcp_update_segment(PXE_TCP_CONNECTION *connection, PXE_TCP_QUEUED_SEGMENT *segment); + +/* when resending updates ack and checksum */ +void tcp_resend_stats(PXE_TCP_CONNECTION *connection); + #endif ==== //depot/projects/soc2007/taleks-pxe_http/pxe_tcp.c#4 (text+ko) ==== @@ -6,6 +6,7 @@ #include "pxe_core.h" #include "pxe_filter.h" #include "pxe_ip.h" +#include "pxe_segment.h" #include "pxe_tcp.h" /* state handle functions */ @@ -244,17 +245,16 @@ return (0); } -/* printf("tcp_check_5(): una = %d, ack = %d\n", connection->una, ack); - if ( connection->una < ack) { */ +/* printf("tcp_check_5(): una = %d, ack = %d\n", connection->una, ack); */ + if ( connection->una <= ack) { connection->una = ack; pxe_resend_update(connection); -/* } else { /* ignore dublicate packet */ -/* #ifdef PXE_DEBUG + } else { /* ignore dublicate packet */ +#ifdef PXE_DEBUG printf("tcp_check_5(): failed\n"); #endif return (0); } -*/ connection->remote_window = tcp_packet->tcphdr.window_size; @@ -274,7 +274,7 @@ if (tcp_packet->tcphdr.flags & PXE_TCP_URG) return (1); -#ifdef PXE_DEBUG +#ifdef PXE_DEBUG_HELL printf("tcp_check_6(): failed\n"); #endif return (0); @@ -296,10 +296,13 @@ if (seglen == 0 ) connection->next_recv += 1; - +/* #ifdef PXE_DEBUG - printf("tcp_process_7(): ack = %d, seq = %d, seglen = %d\n", connection->next_recv, connection->next_send, seglen); + printf("tcp_process_7(): ack = %d, seq = %d, seglen = %d\n", + connection->next_recv - connection->irs, connection->next_send - connection->iss, seglen + ); #endif +*/ if (seglen > 0) { /* write data to buffer */ void *data = ((void *)tcp_packet) + sizeof(PXE_IP_HDR) + 4 * (tcp_packet->tcphdr.data_off >> 4); @@ -322,7 +325,7 @@ { if (tcp_packet->tcphdr.flags & PXE_TCP_FIN) return (1); -#ifdef PXE_DEBUG +#ifdef PXE_DEBUG_HELL printf("tcp_check_8(): failed\n"); #endif return (0); @@ -505,8 +508,7 @@ connection->next_send = tcp_packet->tcphdr.ack_next; } - /* if acked FIN */ -/* if (tcp_check_8(tcp_packet)) { */ + /* if acked our FIN */ if (connection->state_out == PXE_TCP_FIN) { connection->state = PXE_TCP_FIN_WAIT2; #ifdef PXE_DEBUG @@ -809,8 +811,6 @@ uint16_t src_port = tcp_packet->tcphdr.src_port; uint16_t dst_port = tcp_packet->tcphdr.dst_port; -/* uint16_t data_size = pack->data_size - sizeof(PXE_IP_HDR) - 4 * (tcp_packet->tcphdr.data_off >> 4);*/ - PXE_IP_HDR *iphdr = pack->data; /* calculating data size from ip length minus headers length */ @@ -835,16 +835,13 @@ #ifdef PXE_DEBUG printf("pxe_tcp_callback(): packet filtered out, sending RST.\n"); #endif -/* if (flags & PXE_TCP_SYN) { /* this was hopeless attempt to connect */ + if (flags & PXE_TCP_ACK) { + tcp_send_rst_for(tcp_packet, 0, tcp_packet->tcphdr.ack_next, PXE_TCP_RST, data_size); + } else { + tcp_send_rst_for(tcp_packet, tcp_packet->tcphdr.ack_next + data_size, + 0, PXE_TCP_RST | PXE_TCP_ACK, data_size); + } - if (flags & PXE_TCP_ACK) { - tcp_send_rst_for(tcp_packet, 0, tcp_packet->tcphdr.ack_next, PXE_TCP_RST, data_size); - } else { - tcp_send_rst_for(tcp_packet, tcp_packet->tcphdr.ack_next + data_size, - 0, PXE_TCP_RST | PXE_TCP_ACK, data_size); - } -/* } /* otherwise just ignore this packet */ - return (0); } @@ -902,10 +899,10 @@ if (connection->state > PXE_TCP_SYN_SENT) { /* if we know sequence number, then check it */ if (seq != connection->next_recv) { /* not next in order, drop it, send ACK */ -/* tcp_send_ack_for(tcp_packet, connection->next_recv, /* with next needed sequence number */ -/* connection->next_send, sock); */ #ifdef PXE_DEBUG - printf("pxe_tcp_callback(): got %d != awaited %d\n", seq, connection->next_recv); + printf("pxe_tcp_callback(): got %d != awaited %d\n", + seq - connection->irs, connection->next_recv - connection->irs + ); #endif return (0); } @@ -913,10 +910,12 @@ /* in case of SYN_SENT state we don't know sequence number yet */ } + int result = 0; + /* calling appropriate state handler, if it's not NULL */ if (connection->state < PXE_TCP_ALL_STATES) { - int result = 0; + while (1) { #ifdef PXE_DEBUG printf("pxe_tcp_callback(): connection state = 0x%x\n", connection->state); @@ -927,14 +926,20 @@ if (result == 2) continue; - return (result); + break; } else { /* state handler not registered */ - return (0); + break; } } } - return (0); + /* check ACKed packets*/ + pxe_resend_update(connection); + + /* check if need to resend some segments */ + pxe_resend_check(connection); + + return (result); } /* pxe_tcp_init() - initialization of TCP module @@ -952,7 +957,7 @@ pxe_core_register(PXE_TCP_PROTOCOL, pxe_tcp_callback); } -/* pxe_tcp_send() - send data via TCP protocol +/* pxe_tcp_send() - send system packets via TCP protocol * in: * connection - connection to send to * size - data size @@ -964,5 +969,31 @@ int pxe_tcp_send(PXE_TCP_CONNECTION *connection, uint16_t size, uint8_t tcp_flags) { - return (0); + /* allocating smmall segment */ + PXE_TCP_QUEUED_SEGMENT *segment = tcp_segment_alloc(connection, 0); + + if (segment == NULL) { + printf("pxe_tcp_send(): failed to allocate segment.\n"); + return (0); + } + +/* segment->seq = connection->next_send; */ + /* here is simpliest ever in the world way to calculate resend time + * for more reliable resend time calculation need to implement RTT calculating. + */ + + + /* add to every system segment default options */ + tcp_start_segment(connection, segment, 1); + + /* finish segment */ + tcp_finish_segment(connection, segment, tcp_flags); + segment->resend_at = pxe_get_secs() + PXE_RESEND_TIME; + + if (!pxe_tcp_send_segment(connection, segment)) { + printf("pxe_tcp_send(): failed to send segment.\n"); + return (0); + } + + return (1); }
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200706291515.l5TFF6rT046413>