PERFORCE change 122529 for review
Alexey Tarasov
taleks at FreeBSD.org
Fri Jun 29 15:15:08 UTC 2007
http://perforce.freebsd.org/chv.cgi?CH=122529
Change 122529 by taleks at 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);
}
More information about the p4-projects
mailing list