0058001 /*
0058002 tcp_send.c
0058003
0058004 Copyright 1995 Philip Homburg
0058005 */
0058006
0058007 #include "inet.h"
0058008 #include "buf.h"
0058009 #include "clock.h"
0058010 #include "event.h"
0058011 #include "type.h"
0058012
0058013 #include "assert.h"
0058014 #include "io.h"
0058015 #include "ip.h"
0058016 #include "tcp.h"
0058017 #include "tcp_int.h"
0058018
0058019 THIS_FILE
0058020
0058021 FORWARD acc_t *make_pack ARGS(( tcp_conn_t *tcp_conn ));
0058022 FORWARD void tcp_send_timeout ARGS(( int conn, struct timer *timer ));
0058023 FORWARD void do_snd_event ARGS(( event_t *ev, ev_arg_t arg ));
0058024
0058025 PUBLIC void tcp_conn_write (tcp_conn, enq)
0058026 tcp_conn_t *tcp_conn;
0058027 int enq; /* Writes need to be enqueued. */
tcp_conn_write()
0058028 {
0058029 tcp_port_t *tcp_port;
0058030 ev_arg_t snd_arg;
0058031
0058032 assert (tcp_conn->tc_flags & TCF_INUSE);
0058033
0058034 tcp_port= tcp_conn->tc_port;
0058035 if (tcp_conn->tc_flags & TCF_MORE2WRITE)
0058036 return;
0058037
0058038 /* XXX - do we really have something to send here? */
0058039
0058040 tcp_conn->tc_flags |= TCF_MORE2WRITE;
0058041 tcp_conn->tc_send_link= NULL;
0058042 if (!tcp_port->tp_snd_head)
0058043 {
0058044 tcp_port->tp_snd_head= tcp_conn;
0058045 tcp_port->tp_snd_tail= tcp_conn;
0058046 if (enq)
0058047 {
0058048 snd_arg.ev_ptr= tcp_port;
0058049 if (!ev_in_queue(&tcp_port->tp_snd_event))
event_t / ev_enqueue() / ev_process() / ev_init() / ev_in_queue()
The event_t typedef is declared in inet/generic/event.h:
typedef struct event
{
ev_func_t ev_func;
ev_arg_t ev_arg;
struct event *ev_next;
} event_t;
If an event needs to be scheduled, ev_enqueue() is called to place the event in the system-wide event queue whose head is ev_head. ev_process() is eventually called from the main loop in inet.c to process the events. ev_in_queue(ev) simply returns TRUE if the event ev, ev_in_queue()'s only parameter, has a non-null value for func (see below) and FALSE if func is null. In this way, ev_in_queue() determines whether the event has been configured.
ev_init(ev) simply zeroes out the ev_func and ev_next fields of the event ev, ev_init()'s only parameter.
ev_func: A function (e.g., ip_process_loopb()) that performs some task.
ev_arg:
typedef union ev_arg
{
int ev_int;
void *ev_ptr;
} ev_arg_t;
ev_arg is ev_func's argument. In the case of a packet destined for the loopback address (127.0.0.1), the argument will be the ip port associated with the ip file descriptor that is sending out the packet. In the case of a message from the ethernet task that caused a deadlock, ev_arg is a pointer to the message's destination ethernet port.
ev_next: The next event in the system-wide event queue.
0058050 {
0058051 ev_enqueue(&tcp_port->tp_snd_event,
0058052 do_snd_event, snd_arg);
event_t / ev_enqueue() / ev_process() / ev_init() / ev_in_queue()
The event_t typedef is declared in inet/generic/event.h:
typedef struct event
{
ev_func_t ev_func;
ev_arg_t ev_arg;
struct event *ev_next;
} event_t;
If an event needs to be scheduled, ev_enqueue() is called to place the event in the system-wide event queue whose head is ev_head. ev_process() is eventually called from the main loop in inet.c to process the events. ev_in_queue(ev) simply returns TRUE if the event ev, ev_in_queue()'s only parameter, has a non-null value for func (see below) and FALSE if func is null. In this way, ev_in_queue() determines whether the event has been configured.
ev_init(ev) simply zeroes out the ev_func and ev_next fields of the event ev, ev_init()'s only parameter.
ev_func: A function (e.g., ip_process_loopb()) that performs some task.
ev_arg:
typedef union ev_arg
{
int ev_int;
void *ev_ptr;
} ev_arg_t;
ev_arg is ev_func's argument. In the case of a packet destined for the loopback address (127.0.0.1), the argument will be the ip port associated with the ip file descriptor that is sending out the packet. In the case of a message from the ethernet task that caused a deadlock, ev_arg is a pointer to the message's destination ethernet port.
ev_next: The next event in the system-wide event queue.do_snd_event() / tcp
0058053 }
0058054 }
0058055 else
0058056 tcp_port_write(tcp_port);
0058057 }
0058058 else
0058059 {
0058060 tcp_port->tp_snd_tail->tc_send_link= tcp_conn;
0058061 tcp_port->tp_snd_tail= tcp_conn;
0058062 }
0058063 }
0058064
0058065 PRIVATE void do_snd_event(ev, arg)
0058066 event_t *ev;
0058067 ev_arg_t arg;
0058068 {
0058069 tcp_port_t *tcp_port;
0058070
0058071 tcp_port= arg.ev_ptr;
0058072
0058073 assert(ev == &tcp_port->tp_snd_event);
0058074 tcp_port_write(tcp_port);
0058075 }
0058076
0058077 PUBLIC void tcp_port_write(tcp_port)
0058078 tcp_port_t *tcp_port;
0058079 {
0058080 tcp_conn_t *tcp_conn;
0058081 acc_t *pack2write;
0058082 int r;
0058083
0058084 assert (!(tcp_port->tp_flags & TPF_WRITE_IP));
0058085
0058086 while(tcp_port->tp_snd_head)
0058087 {
0058088 tcp_conn= tcp_port->tp_snd_head;
0058089 assert(tcp_conn->tc_flags & TCF_MORE2WRITE);
0058090
0058091 for(;;)
0058092 {
0058093 if (tcp_conn->tc_frag2send)
0058094 {
0058095 pack2write= tcp_conn->tc_frag2send;
0058096 tcp_conn->tc_frag2send= 0;
0058097 }
0058098 else
0058099 {
0058100 tcp_conn->tc_busy++;
0058101 pack2write= make_pack(tcp_conn);
0058102 tcp_conn->tc_busy--;
0058103 if (!pack2write)
0058104 break;
0058105 }
0058106 r= ip_send(tcp_port->tp_ipfd, pack2write,
0058107 bf_bufsize(pack2write));
bf_bufsize()
bf_bufsize() returns the total buffer size of a linked list of accessors (i.e., the sum of acc_length for the accessors in a linked list).
For a detailed description of the network service's buffer management, click here.
0058108 if (r != NW_OK)
0058109 {
0058110 if (r == NW_WOULDBLOCK)
0058111 break;
0058112 if (r == EDSTNOTRCH)
0058113 {
0058114 tcp_notreach(tcp_conn);
0058115 continue;
0058116 }
0058117 else if (r == EBADDEST)
0058118 continue;
0058119 }
0058120 assert(r == NW_OK ||
0058121 (printf("ip_send failed, error %d\n", r),0));
0058122 }
0058123
0058124 if (pack2write)
0058125 {
0058126 tcp_port->tp_flags |= TPF_WRITE_IP;
0058127 tcp_port->tp_pack= pack2write;
0058128
0058129 r= ip_write (tcp_port->tp_ipfd,
0058130 bf_bufsize(pack2write));
bf_bufsize()
bf_bufsize() returns the total buffer size of a linked list of accessors (i.e., the sum of acc_length for the accessors in a linked list).
For a detailed description of the network service's buffer management, click here.
0058131 if (r == NW_SUSPEND)
0058132 {
0058133 tcp_port->tp_flags |= TPF_WRITE_SP;
0058134 return;
0058135 }
0058136 assert(r == NW_OK);
0058137 tcp_port->tp_flags &= ~TPF_WRITE_IP;
0058138 assert(!(tcp_port->tp_flags &
0058139 (TPF_WRITE_IP|TPF_WRITE_SP)));
0058140 continue;
0058141 }
0058142 tcp_conn->tc_flags &= ~TCF_MORE2WRITE;
0058143 tcp_port->tp_snd_head= tcp_conn->tc_send_link;
0058144
0058145 }
0058146 }
0058147
0058148 PRIVATE acc_t *make_pack(tcp_conn)
0058149 tcp_conn_t *tcp_conn;
0058150 {
0058151 acc_t *pack2write, *tmp_pack, *tcp_pack;
0058152 tcp_hdr_t *tcp_hdr;
0058153 ip_hdr_t *ip_hdr;
0058154 int tot_hdr_size, ip_hdr_len;
0058155 u32_t seg_seq, seg_lo_data, queue_lo_data, seg_hi, seg_hi_data;
0058156 u16_t seg_up;
0058157 u8_t seg_flags;
0058158 time_t new_dis;
0058159 size_t pack_size;
0058160 time_t curr_time;
0058161 u8_t *optptr;
0058162
0058163 assert(tcp_conn->tc_busy);
0058164 curr_time= get_time();
get_time()
get_time() returns the number of clock ticks since reboot.
Several of the clients (eth, arp, ip, tcp, and udp) use get_time() to determine an appropriate timeout value for a given operation. For example, the arp code calls get_time() to determine an appropriate amount of time to wait for a response from an arp request before giving up.
0058165 switch (tcp_conn->tc_state)
0058166 {
0058167 case TCS_CLOSED:
0058168 return 0;
0058169 case TCS_SYN_RECEIVED:
0058170 case TCS_SYN_SENT:
0058171
0058172 if (tcp_conn->tc_SND_TRM == tcp_conn->tc_SND_NXT &&
0058173 !(tcp_conn->tc_flags & TCF_SEND_ACK))
0058174 {
0058175 return 0;
0058176 }
0058177
0058178 tcp_conn->tc_flags &= ~TCF_SEND_ACK;
0058179
0058180 /* Include a max segment size option. */
0058181 assert(tcp_conn->tc_tcpopt == NULL);
0058182 tcp_conn->tc_tcpopt= bf_memreq(4);
bf_memreq()
After the buffers have been initialized, accessors[] looks like the following:
bf_memreq() allocates accessors to the caller. For example, if 1514 bytes of buffer space are requested immediately after the network process starts and each buffer is 512 bytes (the default), then accessors[] will look like the following:
Note that three elements of accessors[] have been removed from buf512_freelist and that the head of the chain of the 3 accessors is returned by bf_memreq(). Also note that the acc_linkC and buf_linkC fields have been set to one and acc_length and acc_offset have been set to their appropriate values.
So what happens if there are not enough buffers on the buf512_freelist to satisfy a request? On lines 2280-2290 of buf.c, functions that free buffers for the specific clients (e.g., eth_buffree()) are called until there are enough buffers on buf512_freelist.
For a complete description of the network service's buffer management, click here.
0058183 optptr= (u8_t *)ptr2acc_data(tcp_conn->tc_tcpopt);
ptr2acc_data()
The macro ptr2acc_data is #define'd in inet/generic/buf.h as:
#define ptr2acc_data(/* acc_t * */ a) (bf_temporary_acc=(a), \
(&bf_temporary_acc->acc_buffer->buf_data_p[bf_temporary_acc-> \
acc_offset]))
ptr2acc_data() simply returns a pointer to the actual data within an accessor.
ptr2acc_data() is usually called so that the fields of a header (e.g., ip header) can be analyzed.
0058184 optptr[0]= TCP_OPT_MSS;
0058185 optptr[1]= 4;
0058186 optptr[2]= tcp_conn->tc_mss >> 8;
0058187 optptr[3]= tcp_conn->tc_mss & 0xFF;
0058188
0058189 pack2write= tcp_make_header(tcp_conn, &ip_hdr, &tcp_hdr,
0058190 (acc_t *)0);
0058191
0058192 bf_afree(tcp_conn->tc_tcpopt);
bf_afree()
After a chain of accessors is no longer needed, the chain (and not simply the single accessor passed as the parameter) can be freed by calling bf_free(). However, if either acc_linkC or buf_linkC of one of the accessors in the linked list is not equal to one (1), the entire chain will not be freed. For example, if buf_afree(acc1) is called for the following chain:
Then the resulting chain will be:
bf_afree() returns acc1 (accessors[63]) to acc_freelist (recall that acc_freelist is the linked list of acc_t's without an associated buffer). However, buffers512[127] cannot be freed because acc2 (accessors[64]) still references it.
bf_afree() is called after an accessor's associated data is no longer needed (for example, after a packet has been sent off by the ethernet driver).
0058193 tcp_conn->tc_tcpopt= NULL;
0058194
0058195 if (!pack2write)
0058196 {
0058197 DBLOCK(1, printf("connection closed while inuse\n"));
0058198 return 0;
0058199 }
0058200 tot_hdr_size= bf_bufsize(pack2write);
bf_bufsize()
bf_bufsize() returns the total buffer size of a linked list of accessors (i.e., the sum of acc_length for the accessors in a linked list).
For a detailed description of the network service's buffer management, click here.
0058201 seg_seq= tcp_conn->tc_SND_TRM;
0058202 if (tcp_conn->tc_state == TCS_SYN_SENT)
0058203 seg_flags= 0;
0058204 else
0058205 seg_flags= THF_ACK; /* except for TCS_SYN_SENT
0058206 * ack is always present */
0058207
0058208 if (seg_seq == tcp_conn->tc_ISS)
0058209 {
0058210 assert(tcp_conn->tc_transmit_timer.tim_active ||
0058211 (tcp_print_conn(tcp_conn), printf("\n"), 0));
0058212 seg_flags |= THF_SYN;
0058213 tcp_conn->tc_SND_TRM++;
0058214 }
0058215
0058216 tcp_hdr->th_seq_nr= htonl(seg_seq);
htons() / ntohs() / htonl() / ntohl()
From htons(3):
"htons() converts a 16-bit quantity from host byte order to network byte order."
Different CPU architectures group multiple bytes differently. For example, on a "little-endian" machine (an example of which is the Intel CPU), the value 0x1234 is stored in memory as 0x3412. However, on a "big-endian" machine, the value 0x1234 is stored in memory as 0x1234.
It is important that values in a header are sent across a network in a consistent manner independent of the architecture of the sending or receiving system. For this reason, a standard was chosen. The standard chosen was big-endian although it could have just as well been little-endian.
htons() is defined in /include/net/hton.h, as:
#define htons(x) (_tmp=(x), ((_tmp>>8) & 0xff) | ((_tmp<<8) & 0xff00))
ntohs() converts a 16-bit quantity from network byte order to host byte order, the reverse of htons().
htonl() and ntohl() are identical to htons() and ntohs() except that they convert 32-bit quantities instead of 16-bit quantities.
Processes generally supply header information when sending packets. The data in these fields is converted to the network format (i.e., big-endian) by the process before the process copies the data to the network service.
0058217 tcp_hdr->th_ack_nr= htonl(tcp_conn->tc_RCV_NXT);
htons() / ntohs() / htonl() / ntohl()
From htons(3):
"htons() converts a 16-bit quantity from host byte order to network byte order."
Different CPU architectures group multiple bytes differently. For example, on a "little-endian" machine (an example of which is the Intel CPU), the value 0x1234 is stored in memory as 0x3412. However, on a "big-endian" machine, the value 0x1234 is stored in memory as 0x1234.
It is important that values in a header are sent across a network in a consistent manner independent of the architecture of the sending or receiving system. For this reason, a standard was chosen. The standard chosen was big-endian although it could have just as well been little-endian.
htons() is defined in /include/net/hton.h, as:
#define htons(x) (_tmp=(x), ((_tmp>>8) & 0xff) | ((_tmp<<8) & 0xff00))
ntohs() converts a 16-bit quantity from network byte order to host byte order, the reverse of htons().
htonl() and ntohl() are identical to htons() and ntohs() except that they convert 32-bit quantities instead of 16-bit quantities.
Processes generally supply header information when sending packets. The data in these fields is converted to the network format (i.e., big-endian) by the process before the process copies the data to the network service.
0058218 tcp_hdr->th_flags= seg_flags;
0058219 tcp_hdr->th_window= htons(tcp_conn->tc_mss);
htons() / ntohs() / htonl() / ntohl()
From htons(3):
"htons() converts a 16-bit quantity from host byte order to network byte order."
Different CPU architectures group multiple bytes differently. For example, on a "little-endian" machine (an example of which is the Intel CPU), the value 0x1234 is stored in memory as 0x3412. However, on a "big-endian" machine, the value 0x1234 is stored in memory as 0x1234.
It is important that values in a header are sent across a network in a consistent manner independent of the architecture of the sending or receiving system. For this reason, a standard was chosen. The standard chosen was big-endian although it could have just as well been little-endian.
htons() is defined in /include/net/hton.h, as:
#define htons(x) (_tmp=(x), ((_tmp>>8) & 0xff) | ((_tmp<<8) & 0xff00))
ntohs() converts a 16-bit quantity from network byte order to host byte order, the reverse of htons().
htonl() and ntohl() are identical to htons() and ntohs() except that they convert 32-bit quantities instead of 16-bit quantities.
Processes generally supply header information when sending packets. The data in these fields is converted to the network format (i.e., big-endian) by the process before the process copies the data to the network service.
0058220 /* Initially we allow one segment */
0058221
0058222 ip_hdr->ih_length= htons(tot_hdr_size);
0058223
0058224 pack2write->acc_linkC++;
0058225 ip_hdr_len= (ip_hdr->ih_vers_ihl & IH_IHL_MASK) << 2;
0058226 tcp_pack= bf_delhead(pack2write, ip_hdr_len);
bf_delhead()
If only the beginning of a linked list can be freed, bf_delhead() is called. If acc_linkC and buf_linkC are one for all of the relevant accessors and their associated buffers in the linked list, the operation is straight-forward:
bf_delhead() is often called to remove the header (e.g., ip header) from a packet.
For a detailed description of the network service's buffer management, click here.
0058227 tcp_hdr->th_chksum= ~tcp_pack_oneCsum(ip_hdr, tcp_pack);
tcp_pack_oneCsum()
tcp_pack_oneCsum() computes the checksum of a tcp packet (including several fields of its ip header). It accomplishes this by computing the checksum (by calling oneC_sum()) of each of the tcp packet's buffers.
Note that a checksum is used to determine if errors occurred during the transmission of data.
0058228 bf_afree(tcp_pack);
bf_afree()
After a chain of accessors is no longer needed, the chain (and not simply the single accessor passed as the parameter) can be freed by calling bf_free(). However, if either acc_linkC or buf_linkC of one of the accessors in the linked list is not equal to one (1), the entire chain will not be freed. For example, if buf_afree(acc1) is called for the following chain:
Then the resulting chain will be:
bf_afree() returns acc1 (accessors[63]) to acc_freelist (recall that acc_freelist is the linked list of acc_t's without an associated buffer). However, buffers512[127] cannot be freed because acc2 (accessors[64]) still references it.
bf_afree() is called after an accessor's associated data is no longer needed (for example, after a packet has been sent off by the ethernet driver).
0058229
0058230 new_dis= curr_time + 2*HZ*tcp_conn->tc_ttl;
0058231 if (new_dis > tcp_conn->tc_senddis)
0058232 tcp_conn->tc_senddis= new_dis;
0058233 return pack2write;
0058234
0058235 case TCS_ESTABLISHED:
0058236 case TCS_CLOSING:
0058237 seg_seq= tcp_conn->tc_SND_TRM;
0058238
0058239 seg_flags= 0;
0058240 pack2write= 0;
0058241 seg_up= 0;
0058242 if (tcp_conn->tc_flags & TCF_SEND_ACK)
0058243 {
0058244 seg_flags= THF_ACK;
0058245 tcp_conn->tc_flags &= ~TCF_SEND_ACK;
0058246
0058247 pack2write= tcp_make_header (tcp_conn, &ip_hdr,
0058248 &tcp_hdr, (acc_t *)0);
0058249 if (!pack2write)
0058250 {
0058251 return NULL;
0058252 }
0058253 }
0058254
0058255 if (tcp_conn->tc_SND_UNA != tcp_conn->tc_SND_NXT)
0058256 {
0058257 assert(tcp_LEmod4G(seg_seq, tcp_conn->tc_SND_NXT));
0058258
0058259 if (seg_seq == tcp_conn->tc_snd_cwnd)
0058260 {
0058261 DBLOCK(2,
0058262 printf("no data: window is closed\n"));
0058263 goto after_data;
0058264 }
0058265
0058266 /* Assert that our SYN has been ACKed. */
0058267 assert(tcp_conn->tc_SND_UNA != tcp_conn->tc_ISS);
0058268
0058269 seg_lo_data= seg_seq;
0058270 queue_lo_data= tcp_conn->tc_SND_UNA;
0058271
0058272 seg_hi= tcp_conn->tc_SND_NXT;
0058273 seg_hi_data= seg_hi;
0058274 if (tcp_conn->tc_flags & TCF_FIN_SENT)
0058275 {
0058276 if (seg_seq != seg_hi)
0058277 seg_flags |= THF_FIN;
0058278 if (queue_lo_data == seg_hi_data)
0058279 queue_lo_data--;
0058280 if (seg_lo_data == seg_hi_data)
0058281 seg_lo_data--;
0058282 seg_hi_data--;
0058283 }
0058284
0058285 if (!pack2write)
0058286 {
0058287 pack2write= tcp_make_header (tcp_conn,
0058288 &ip_hdr, &tcp_hdr, (acc_t *)0);
0058289 if (!pack2write)
0058290 {
0058291 return NULL;
0058292 }
0058293 }
0058294
0058295 tot_hdr_size= bf_bufsize(pack2write);
bf_bufsize()
bf_bufsize() returns the total buffer size of a linked list of accessors (i.e., the sum of acc_length for the accessors in a linked list).
For a detailed description of the network service's buffer management, click here.
0058296 if (seg_hi_data - seg_lo_data > tcp_conn->tc_mss -
0058297 tot_hdr_size)
0058298 {
0058299 seg_hi_data= seg_lo_data + tcp_conn->tc_mss -
0058300 tot_hdr_size;
0058301 seg_hi= seg_hi_data;
0058302 seg_flags &= ~THF_FIN;
0058303 }
0058304
0058305 if (tcp_Gmod4G(seg_hi, tcp_conn->tc_snd_cwnd))
0058306 {
0058307 seg_hi_data= tcp_conn->tc_snd_cwnd;
0058308 seg_hi= seg_hi_data;
0058309 seg_flags &= ~THF_FIN;
0058310 }
0058311
0058312 if (seg_hi-seg_seq == 0)
0058313 {
0058314 DBLOCK(0x20,
0058315 printf("no data: no data available\n"));
0058316 goto after_data;
0058317 }
0058318
0058319 if (seg_seq != tcp_conn->tc_SND_UNA &&
0058320 seg_hi_data-seg_lo_data+tot_hdr_size <
0058321 tcp_conn->tc_mss)
0058322 {
0058323 DBLOCK(0x20,
0058324 printf("no data: partial packet\n"));
0058325 seg_flags &= ~THF_FIN;
0058326 goto after_data;
0058327 }
0058328
0058329 if (tcp_GEmod4G(tcp_conn->tc_SND_UP, seg_lo_data))
0058330 {
0058331 if (tcp_GEmod4G(tcp_conn->tc_SND_UP,
0058332 seg_hi_data))
0058333 {
0058334 seg_up= seg_hi_data-seg_seq;
0058335 }
0058336 else
0058337 {
0058338 seg_up= tcp_conn->tc_SND_UP-seg_seq;
0058339 }
0058340 seg_flags |= THF_URG;
0058341 if ((tcp_conn->tc_flags & TCF_BSD_URG) &&
0058342 seg_up == 0)
0058343 {
0058344 /* A zero urgent pointer doesn't mean
0058345 * anything when BSD semantics are
0058346 * used (urgent pointer points to the
0058347 * first no urgent byte). The use of
0058348 * a zero urgent pointer also crashes
0058349 * a Solaris 2.3 kernel. If urgent
0058350 * pointer doesn't have BSD semantics
0058351 * then an urgent pointer of zero
0058352 * simply indicates that there is one
0058353 * urgent byte.
0058354 */
0058355 seg_flags &= ~THF_URG;
0058356 }
0058357 }
0058358 else
0058359 seg_up= 0;
0058360
0058361 if (tcp_Gmod4G(tcp_conn->tc_SND_PSH, seg_lo_data) &&
0058362 tcp_LEmod4G(tcp_conn->tc_SND_PSH, seg_hi_data))
0058363 {
0058364 seg_flags |= THF_PSH;
0058365 }
0058366
0058367 tcp_conn->tc_SND_TRM= seg_hi;
0058368
0058369 assert(tcp_conn->tc_transmit_timer.tim_active ||
0058370 (tcp_print_conn(tcp_conn), printf("\n"), 0));
0058371 if (tcp_conn->tc_rt_seq == 0 &&
0058372 tcp_Gmod4G(seg_seq, tcp_conn->tc_rt_threshold))
0058373 {
0058374 tcp_conn->tc_rt_time= curr_time;
0058375 tcp_conn->tc_rt_seq=
0058376 tcp_conn->tc_rt_threshold= seg_seq;
0058377 }
0058378
0058379 if (seg_hi_data-seg_lo_data)
0058380 {
0058381 #if DEBUG & 0
0058382 assert(tcp_check_conn(tcp_conn));
0058383 assert((seg_hi_data-queue_lo_data <=
0058384 bf_bufsize(tcp_conn->tc_send_data) &&
0058385 seg_lo_data-queue_lo_data <=
0058386 bf_bufsize(tcp_conn->tc_send_data) &&
0058387 seg_hi_data>seg_lo_data)||
0058388 (tcp_print_conn(tcp_conn),
0058389 printf(
0058390 " seg_hi_data= 0x%x, seg_lo_data= 0x%x, queue_lo_data= 0x%x\n",
0058391 seg_hi_data, seg_lo_data,
0058392 queue_lo_data), 0));
0058393 #endif
0058394
0058395 tmp_pack= pack2write;
0058396 while (tmp_pack->acc_next)
0058397 tmp_pack= tmp_pack->acc_next;
0058398 tmp_pack->acc_next=
0058399 bf_cut(tcp_conn->tc_send_data,
0058400 (unsigned)(seg_lo_data-queue_lo_data),
0058401 (unsigned) (seg_hi_data-seg_lo_data));
bf_cut()
If a section of a linked list needs to be duplicated, bf_cut(data, offset, length) is called. For example, if a section of length 50 starting at an offset of 75 of the linked list below needs to be duplicated, bf_cut(data, 75, 50) is called:
Note that the original linked list remains unchanged and that acc_linkC for all the accessors in the new linked list is one.
If length (the second parameter) is zero, simply duplicate the first accessor in the linked list but set acc_length=0 and acc_next=null. In other words, create a linked list of length one accessor whose acc_length is 0.
bf_cut() is used in a number of scenarios, including cutting a received ethernet packet to size.
For a full description of the network service's buffer management, click here.
0058402 }
0058403 seg_flags |= THF_ACK;
0058404 }
0058405
0058406 after_data:
0058407 if (!(seg_flags & THF_ACK))
0058408 {
0058409 if (pack2write)
0058410 bf_afree(pack2write);
bf_afree()
After a chain of accessors is no longer needed, the chain (and not simply the single accessor passed as the parameter) can be freed by calling bf_free(). However, if either acc_linkC or buf_linkC of one of the accessors in the linked list is not equal to one (1), the entire chain will not be freed. For example, if buf_afree(acc1) is called for the following chain:
Then the resulting chain will be:
bf_afree() returns acc1 (accessors[63]) to acc_freelist (recall that acc_freelist is the linked list of acc_t's without an associated buffer). However, buffers512[127] cannot be freed because acc2 (accessors[64]) still references it.
bf_afree() is called after an accessor's associated data is no longer needed (for example, after a packet has been sent off by the ethernet driver).
0058411 return NULL;
0058412 }
0058413
0058414 tcp_hdr->th_seq_nr= htonl(seg_seq);
htons() / ntohs() / htonl() / ntohl()
From htons(3):
"htons() converts a 16-bit quantity from host byte order to network byte order."
Different CPU architectures group multiple bytes differently. For example, on a "little-endian" machine (an example of which is the Intel CPU), the value 0x1234 is stored in memory as 0x3412. However, on a "big-endian" machine, the value 0x1234 is stored in memory as 0x1234.
It is important that values in a header are sent across a network in a consistent manner independent of the architecture of the sending or receiving system. For this reason, a standard was chosen. The standard chosen was big-endian although it could have just as well been little-endian.
htons() is defined in /include/net/hton.h, as:
#define htons(x) (_tmp=(x), ((_tmp>>8) & 0xff) | ((_tmp<<8) & 0xff00))
ntohs() converts a 16-bit quantity from network byte order to host byte order, the reverse of htons().
htonl() and ntohl() are identical to htons() and ntohs() except that they convert 32-bit quantities instead of 16-bit quantities.
Processes generally supply header information when sending packets. The data in these fields is converted to the network format (i.e., big-endian) by the process before the process copies the data to the network service.
0058415 tcp_hdr->th_ack_nr= htonl(tcp_conn->tc_RCV_NXT);
htons() / ntohs() / htonl() / ntohl()
From htons(3):
"htons() converts a 16-bit quantity from host byte order to network byte order."
Different CPU architectures group multiple bytes differently. For example, on a "little-endian" machine (an example of which is the Intel CPU), the value 0x1234 is stored in memory as 0x3412. However, on a "big-endian" machine, the value 0x1234 is stored in memory as 0x1234.
It is important that values in a header are sent across a network in a consistent manner independent of the architecture of the sending or receiving system. For this reason, a standard was chosen. The standard chosen was big-endian although it could have just as well been little-endian.
htons() is defined in /include/net/hton.h, as:
#define htons(x) (_tmp=(x), ((_tmp>>8) & 0xff) | ((_tmp<<8) & 0xff00))
ntohs() converts a 16-bit quantity from network byte order to host byte order, the reverse of htons().
htonl() and ntohl() are identical to htons() and ntohs() except that they convert 32-bit quantities instead of 16-bit quantities.
Processes generally supply header information when sending packets. The data in these fields is converted to the network format (i.e., big-endian) by the process before the process copies the data to the network service.
0058416 tcp_hdr->th_flags= seg_flags;
0058417 tcp_hdr->th_window= htons(tcp_conn->tc_RCV_HI -
0058418 tcp_conn->tc_RCV_NXT);
0058419 tcp_hdr->th_urgptr= htons(seg_up);
htons() / ntohs() / htonl() / ntohl()
From htons(3):
"htons() converts a 16-bit quantity from host byte order to network byte order."
Different CPU architectures group multiple bytes differently. For example, on a "little-endian" machine (an example of which is the Intel CPU), the value 0x1234 is stored in memory as 0x3412. However, on a "big-endian" machine, the value 0x1234 is stored in memory as 0x1234.
It is important that values in a header are sent across a network in a consistent manner independent of the architecture of the sending or receiving system. For this reason, a standard was chosen. The standard chosen was big-endian although it could have just as well been little-endian.
htons() is defined in /include/net/hton.h, as:
#define htons(x) (_tmp=(x), ((_tmp>>8) & 0xff) | ((_tmp<<8) & 0xff00))
ntohs() converts a 16-bit quantity from network byte order to host byte order, the reverse of htons().
htonl() and ntohl() are identical to htons() and ntohs() except that they convert 32-bit quantities instead of 16-bit quantities.
Processes generally supply header information when sending packets. The data in these fields is converted to the network format (i.e., big-endian) by the process before the process copies the data to the network service.
0058420
0058421 pack_size= bf_bufsize(pack2write);
bf_bufsize()
bf_bufsize() returns the total buffer size of a linked list of accessors (i.e., the sum of acc_length for the accessors in a linked list).
For a detailed description of the network service's buffer management, click here.
0058422 ip_hdr->ih_length= htons(pack_size);
bf_bufsize()
bf_bufsize() returns the total buffer size of a linked list of accessors (i.e., the sum of acc_length for the accessors in a linked list).
For a detailed description of the network service's buffer management, click here.
0058423
0058424 pack2write->acc_linkC++;
0058425 ip_hdr_len= (ip_hdr->ih_vers_ihl & IH_IHL_MASK) << 2;
0058426 tcp_pack= bf_delhead(pack2write, ip_hdr_len);
bf_delhead()
If only the beginning of a linked list can be freed, bf_delhead() is called. If acc_linkC and buf_linkC are one for all of the relevant accessors and their associated buffers in the linked list, the operation is straight-forward:
bf_delhead() is often called to remove the header (e.g., ip header) from a packet.
For a detailed description of the network service's buffer management, click here.
0058427 tcp_hdr->th_chksum= ~tcp_pack_oneCsum(ip_hdr, tcp_pack);
tcp_pack_oneCsum()
tcp_pack_oneCsum() computes the checksum of a tcp packet (including several fields of its ip header). It accomplishes this by computing the checksum (by calling oneC_sum()) of each of the tcp packet's buffers.
Note that a checksum is used to determine if errors occurred during the transmission of data.
0058428 bf_afree(tcp_pack);
bf_afree()
After a chain of accessors is no longer needed, the chain (and not simply the single accessor passed as the parameter) can be freed by calling bf_free(). However, if either acc_linkC or buf_linkC of one of the accessors in the linked list is not equal to one (1), the entire chain will not be freed. For example, if buf_afree(acc1) is called for the following chain:
Then the resulting chain will be:
bf_afree() returns acc1 (accessors[63]) to acc_freelist (recall that acc_freelist is the linked list of acc_t's without an associated buffer). However, buffers512[127] cannot be freed because acc2 (accessors[64]) still references it.
bf_afree() is called after an accessor's associated data is no longer needed (for example, after a packet has been sent off by the ethernet driver).
0058429
0058430 new_dis= curr_time + 2*HZ*tcp_conn->tc_ttl;
0058431 if (new_dis > tcp_conn->tc_senddis)
0058432 tcp_conn->tc_senddis= new_dis;
0058433
0058434 return pack2write;
0058435 #if !CRAMPED
0058436 default:
0058437 DBLOCK(1, tcp_print_conn(tcp_conn); printf("\n"));
0058438 ip_panic(( "Illegal state" ));
0058439 #endif
0058440 }
0058441 assert(0);
0058442 return NULL;
0058443 }
0058444
0058445 /*
0058446 tcp_release_retrans
0058447 */
0058448
0058449 PUBLIC void tcp_release_retrans(tcp_conn, seg_ack, new_win)
0058450 tcp_conn_t *tcp_conn;
0058451 u32_t seg_ack;
0058452 u16_t new_win;
0058453 {
0058454 size_t size, offset;
0058455 acc_t *pack;
0058456 time_t retrans_time, curr_time, rtt;
0058457 u32_t queue_lo, queue_hi;
0058458 u16_t mss, cthresh;
0058459 unsigned window;
0058460
0058461 assert(tcp_conn->tc_busy);
0058462 assert (tcp_GEmod4G(seg_ack, tcp_conn->tc_SND_UNA));
0058463 assert (tcp_LEmod4G(seg_ack, tcp_conn->tc_SND_NXT));
0058464
0058465 curr_time= get_time();
get_time()
get_time() returns the number of clock ticks since reboot.
Several of the clients (eth, arp, ip, tcp, and udp) use get_time() to determine an appropriate timeout value for a given operation. For example, the arp code calls get_time() to determine an appropriate amount of time to wait for a response from an arp request before giving up.
0058466 if (tcp_conn->tc_rt_seq != 0 &&
0058467 tcp_Gmod4G(seg_ack, tcp_conn->tc_rt_seq))
0058468 {
0058469 assert(curr_time >= tcp_conn->tc_rt_time);
0058470 retrans_time= curr_time-tcp_conn->tc_rt_time;
0058471 rtt= tcp_conn->tc_rtt;
0058472
0058473 DBLOCK(0x20, printf(
0058474 "tcp_release_retrans, conn[%d]: retrans_time= %ld ms\n",
0058475 tcp_conn-tcp_conn_table, retrans_time*1000/HZ));
0058476
0058477
0058478 tcp_conn->tc_rt_seq= 0;
0058479
0058480 if (rtt == TCP_RTT_GRAN*CLOCK_GRAN &&
0058481 retrans_time <= TCP_RTT_GRAN*CLOCK_GRAN)
0058482 {
0058483 /* Common in fast networks. Nothing to do. */
0058484 }
0058485 else if (rtt >= retrans_time && rtt <= 2*retrans_time)
0058486 {
0058487 /* Nothing to do. We assume that a factor 2 for
0058488 * variance is enough.
0058489 */
0058490 }
0058491 else if (retrans_time > rtt)
0058492 {
0058493 /* Retrans time is really too small. */
0058494
0058495 tcp_conn->tc_rtt= rtt*2;
0058496 if (tcp_conn->tc_rtt > TCP_RTT_MAX)
0058497 {
0058498 #if DEBUG
0058499 static int warned /* = 0 */;
0058500
0058501 if (!warned)
0058502 {
0058503 printf(
0058504 "tcp_release_retrans: warning retransmission time is limited to %d ms\n",
0058505 TCP_RTT_MAX*1000/HZ);
0058506 warned= 1;
0058507 }
0058508 #endif
0058509 tcp_conn->tc_rtt= TCP_RTT_MAX;
0058510 }
0058511 assert (tcp_conn->tc_rtt);
0058512
0058513 DBLOCK(0x10, printf(
0058514 "tcp_release_retrans, conn[%d]: (was too small) retrans_time= %ld ms, rtt= %ld ms\n",
0058515 tcp_conn-tcp_conn_table, retrans_time*1000/HZ,
0058516 tcp_conn->tc_rtt*1000/HZ));
0058517
0058518
0058519 }
0058520 else if (seg_ack - tcp_conn->tc_rt_seq == tcp_conn->tc_mss)
0058521 {
0058522 /* Retrans time is really too big. */
0058523 rtt= (rtt*3)>>2;
0058524 if (rtt < TCP_RTT_GRAN*CLOCK_GRAN)
0058525 rtt= TCP_RTT_GRAN*CLOCK_GRAN;
0058526 tcp_conn->tc_rtt= rtt;
0058527 assert (tcp_conn->tc_rtt);
0058528
0058529 DBLOCK(0x10, printf(
0058530 "tcp_release_retrans, conn[%d]: (was too big) retrans_time= %ld ms, rtt= %ld ms\n",
0058531 tcp_conn-tcp_conn_table, retrans_time*1000/HZ,
0058532 tcp_conn->tc_rtt*1000/HZ));
0058533 }
0058534 else
0058535 {
0058536 /* Retrans time might be too big. Try a bit smaller. */
0058537 rtt= (rtt*31)>>5;
0058538 if (rtt < TCP_RTT_GRAN*CLOCK_GRAN)
0058539 rtt= TCP_RTT_GRAN*CLOCK_GRAN;
0058540 tcp_conn->tc_rtt= rtt;
0058541 assert (tcp_conn->tc_rtt);
0058542
0058543 DBLOCK(0x20, printf(
0058544 "tcp_release_retrans, conn[%d]: (maybe too big) retrans_time= %ld ms, rtt= %ld ms\n",
0058545 tcp_conn-tcp_conn_table, retrans_time*1000/HZ,
0058546 tcp_conn->tc_rtt*1000/HZ));
0058547 }
0058548 }
0058549
0058550 /* Update the current window. */
0058551 window= tcp_conn->tc_snd_cwnd-tcp_conn->tc_SND_UNA;
0058552 mss= tcp_conn->tc_mss;
0058553 assert(seg_ack != tcp_conn->tc_SND_UNA);
0058554
0058555 /* For every real ACK we try to increase the current window
0058556 * with 1 mss.
0058557 */
0058558 window += mss;
0058559
0058560 /* If the window becomes larger than the current threshold,
0058561 * increment the threshold by a small amount and set the
0058562 * window to the threshold.
0058563 */
0058564 cthresh= tcp_conn->tc_snd_cthresh;
0058565 if (window > cthresh)
0058566 {
0058567 cthresh += tcp_conn->tc_snd_cinc;
0058568 tcp_conn->tc_snd_cthresh= cthresh;
0058569 window= cthresh;
0058570 }
0058571
0058572 /* If the window is larger than the window advertised by the
0058573 * receiver, set the window size to the advertisement.
0058574 */
0058575 if (window > new_win)
0058576 window= new_win;
0058577
0058578 tcp_conn->tc_snd_cwnd= seg_ack+window;
0058579
0058580 /* Release data queued for retransmissions. */
0058581 queue_lo= tcp_conn->tc_SND_UNA;
0058582 queue_hi= tcp_conn->tc_SND_NXT;
0058583
0058584 tcp_conn->tc_SND_UNA= seg_ack;
0058585 if (tcp_Lmod4G(tcp_conn->tc_SND_TRM, seg_ack))
0058586 {
0058587 tcp_conn->tc_SND_TRM= seg_ack;
0058588 }
0058589 assert(tcp_GEmod4G(tcp_conn->tc_snd_cwnd, seg_ack));
0058590
0058591 if (queue_lo == tcp_conn->tc_ISS)
0058592 queue_lo++;
0058593
0058594 if (tcp_conn->tc_flags & TCF_FIN_SENT)
0058595 {
0058596 if (seg_ack == queue_hi)
0058597 seg_ack--;
0058598 if (queue_lo == queue_hi)
0058599 queue_lo--;
0058600 queue_hi--;
0058601 }
0058602
0058603 offset= seg_ack - queue_lo;
0058604 size= queue_hi - seg_ack;
0058605 pack= tcp_conn->tc_send_data;
0058606 tcp_conn->tc_send_data= 0;
0058607
0058608 if (!size)
0058609 {
0058610 bf_afree(pack);
bf_afree()
After a chain of accessors is no longer needed, the chain (and not simply the single accessor passed as the parameter) can be freed by calling bf_free(). However, if either acc_linkC or buf_linkC of one of the accessors in the linked list is not equal to one (1), the entire chain will not be freed. For example, if buf_afree(acc1) is called for the following chain:
Then the resulting chain will be:
bf_afree() returns acc1 (accessors[63]) to acc_freelist (recall that acc_freelist is the linked list of acc_t's without an associated buffer). However, buffers512[127] cannot be freed because acc2 (accessors[64]) still references it.
bf_afree() is called after an accessor's associated data is no longer needed (for example, after a packet has been sent off by the ethernet driver).
0058611
0058612 /* Reset window if a write is completed */
0058613 tcp_conn->tc_snd_cwnd= tcp_conn->tc_SND_UNA +
0058614 2*tcp_conn->tc_mss;
0058615 }
0058616 else
0058617 {
0058618 pack= bf_delhead(pack, offset);
bf_delhead()
If only the beginning of a linked list can be freed, bf_delhead() is called. If acc_linkC and buf_linkC are one for all of the relevant accessors and their associated buffers in the linked list, the operation is straight-forward:
bf_delhead() is often called to remove the header (e.g., ip header) from a packet.
For a detailed description of the network service's buffer management, click here.
0058619 tcp_conn->tc_send_data= pack;
0058620 }
0058621
0058622 if (tcp_Gmod4G(tcp_conn->tc_SND_TRM, tcp_conn->tc_snd_cwnd))
0058623 tcp_conn->tc_SND_TRM= tcp_conn->tc_snd_cwnd;
0058624
0058625 /* Copy in new data if a write request is pending and
0058626 * SND_NXT-SND_TRM is less than 1 mss.
0058627 */
0058628 if (tcp_conn->tc_fd)
0058629 {
0058630 if ((tcp_conn->tc_fd->tf_flags &
0058631 (TFF_WRITE_IP|TFF_IOCTL_IP)) &&
0058632 tcp_conn->tc_SND_NXT-tcp_conn->tc_SND_TRM <
0058633 tcp_conn->tc_mss)
0058634 {
0058635 tcp_fd_write(tcp_conn);
0058636 }
0058637 }
0058638 else
0058639 {
0058640 if (tcp_conn->tc_SND_UNA == tcp_conn->tc_SND_NXT)
0058641 {
0058642 assert(tcp_conn->tc_state == TCS_CLOSING);
0058643 DBLOCK(0x10,
0058644 printf("all data sent in abondoned connection\n"));
0058645 tcp_close_connection(tcp_conn, ENOTCONN);
0058646 return;
0058647 }
0058648 }
0058649
0058650 DIFBLOCK(2, (tcp_conn->tc_snd_cwnd == tcp_conn->tc_SND_TRM),
0058651 printf("not sending: zero window\n"));
0058652
0058653 if (tcp_conn->tc_snd_cwnd != tcp_conn->tc_SND_TRM &&
0058654 tcp_conn->tc_SND_NXT != tcp_conn->tc_SND_TRM)
0058655 {
0058656 tcp_conn_write(tcp_conn, 1);
0058657 }
0058658
0058659 }
0058660
0058661 /*
0058662 tcp_send_timeout
0058663 */
0058664
0058665 PRIVATE void tcp_send_timeout(conn, timer)
0058666 int conn;
0058667 struct timer *timer;
0058668 {
0058669 tcp_conn_t *tcp_conn;
0058670 u16_t mss, mss2;
0058671 time_t curr_time, stt, timeout;
0058672
0058673 curr_time= get_time();
get_time()
get_time() returns the number of clock ticks since reboot.
Several of the clients (eth, arp, ip, tcp, and udp) use get_time() to determine an appropriate timeout value for a given operation. For example, the arp code calls get_time() to determine an appropriate amount of time to wait for a response from an arp request before giving up.
0058674
0058675 tcp_conn= &tcp_conn_table[conn];
0058676 assert(tcp_conn->tc_flags & TCF_INUSE);
0058677 assert(tcp_conn->tc_state != TCS_CLOSED);
0058678 assert(tcp_conn->tc_state != TCS_LISTEN);
0058679
0058680 if (tcp_conn->tc_SND_NXT == tcp_conn->tc_SND_UNA)
0058681 {
0058682 /* Nothing to do */
0058683 assert(tcp_conn->tc_SND_TRM == tcp_conn->tc_SND_UNA);
0058684
0058685 /* A new write sets the timer if tc_transmit_seq == SND_UNA */
0058686 tcp_conn->tc_transmit_seq= tcp_conn->tc_SND_UNA;
0058687 tcp_conn->tc_stt= 0;
0058688 tcp_conn->tc_0wnd_to= 0;
0058689 assert(!tcp_conn->tc_fd ||
0058690 !(tcp_conn->tc_fd->tf_flags & TFF_WRITE_IP));
0058691 return;
0058692 }
0058693
0058694 if (tcp_conn->tc_transmit_seq != tcp_conn->tc_SND_UNA)
0058695 {
0058696 /* Some data has been acknowledged since the last time the
0058697 * timer was set, set the timer again. */
0058698 tcp_conn->tc_transmit_seq= tcp_conn->tc_SND_UNA;
0058699 tcp_conn->tc_stt= 0;
0058700 tcp_conn->tc_0wnd_to= 0;
0058701
0058702 DBLOCK(0x20, printf(
0058703 "tcp_send_timeout: conn[%d] setting timer to %ld ms (+%ld ms)\n",
0058704 tcp_conn-tcp_conn_table,
0058705 (curr_time+tcp_conn->tc_rtt)*1000/HZ,
0058706 tcp_conn->tc_rtt*1000/HZ));
0058707
0058708 clck_timer(&tcp_conn->tc_transmit_timer,
0058709 curr_time+tcp_conn->tc_rtt,
0058710 tcp_send_timeout, tcp_conn-tcp_conn_table);
clck_timer()
clck_timer(timer, timeout, func, fd) configures timer, clck_timer()'s first parameter, based on timeout, func, and fd (clck_timer()'s second, third, and fourth parameters) and places the timer in its appropriate position in timer_chain. In other words, if the new timer is scheduled to expire before any other timer, it is inserted at the head of the linked list and if the new timer is scheduled to expire after all the other timers, it is inserted at the tail of the linked list. If the new timer is scheduled to expire before any other timers, clck_timer() calls set_timer(), which sends a message to the clock task with a revised expiration time for the synchronous alarm.
An example of a client calling clck_timer() to insert a timer in timer_chain is the arp client.
0058711 return;
0058712 }
0058713
0058714 if (tcp_conn->tc_stt == 0)
0058715 {
0058716 /* Some packet arrived but did not acknowledge any data.
0058717 * Apparently, the other side is still alive and has a
0058718 * reason to transmit. We can asume a zero window.
0058719 */
0058720
0058721 DBLOCK(0x10, printf("conn[%d] setting zero window timer\n",
0058722 tcp_conn-tcp_conn_table));
0058723
0058724 if (tcp_conn->tc_0wnd_to < TCP_0WND_MIN)
0058725 tcp_conn->tc_0wnd_to= TCP_0WND_MIN;
0058726 else if (tcp_conn->tc_0wnd_to < tcp_conn->tc_rtt)
0058727 tcp_conn->tc_0wnd_to= tcp_conn->tc_rtt;
0058728 else
0058729 {
0058730 tcp_conn->tc_0wnd_to *= 2;
0058731 if (tcp_conn->tc_0wnd_to > TCP_0WND_MAX)
0058732 tcp_conn->tc_0wnd_to= TCP_0WND_MAX;
0058733 }
0058734 tcp_conn->tc_stt= curr_time;
0058735
0058736 tcp_conn->tc_rt_seq= 0;
0058737
0058738 DBLOCK(0x20, printf(
0058739 "tcp_send_timeout: conn[%d] setting timer to %ld ms (+%ld ms)\n",
0058740 tcp_conn-tcp_conn_table,
0058741 (curr_time+tcp_conn->tc_0wnd_to)*1000/HZ,
0058742 tcp_conn->tc_0wnd_to*1000/HZ));
0058743
0058744 clck_timer(&tcp_conn->tc_transmit_timer,
0058745 curr_time+tcp_conn->tc_0wnd_to,
0058746 tcp_send_timeout, tcp_conn-tcp_conn_table);
clck_timer()
clck_timer(timer, timeout, func, fd) configures timer, clck_timer()'s first parameter, based on timeout, func, and fd (clck_timer()'s second, third, and fourth parameters) and places the timer in its appropriate position in timer_chain. In other words, if the new timer is scheduled to expire before any other timer, it is inserted at the head of the linked list and if the new timer is scheduled to expire after all the other timers, it is inserted at the tail of the linked list. If the new timer is scheduled to expire before any other timers, clck_timer() calls set_timer(), which sends a message to the clock task with a revised expiration time for the synchronous alarm.
An example of a client calling clck_timer() to insert a timer in timer_chain is the arp client.
0058747 return;
0058748 }
0058749
0058750 DIFBLOCK(0x10, (tcp_conn->tc_fd == 0),
0058751 printf("conn[%d] timeout in abondoned connection\n",
0058752 tcp_conn-tcp_conn_table));
0058753
0058754 /* At this point, we have do a retransmission, or send a zero window
0058755 * probe, which is almost the same.
0058756 */
0058757
0058758 DBLOCK(0x20, printf("tcp_send_timeout: conn[%d] una= %u, rtt= %dms\n",
0058759 tcp_conn-tcp_conn_table,
0058760 tcp_conn->tc_SND_UNA, tcp_conn->tc_rtt*1000/HZ));
0058761
0058762 /* Update threshold sequence number for retransmission calculation. */
0058763 if (tcp_Gmod4G(tcp_conn->tc_SND_TRM, tcp_conn->tc_rt_threshold))
0058764 tcp_conn->tc_rt_threshold= tcp_conn->tc_SND_TRM;
0058765
0058766 tcp_conn->tc_SND_TRM= tcp_conn->tc_SND_UNA;
0058767
0058768 mss= tcp_conn->tc_mss;
0058769 mss2= 2*mss;
0058770
0058771 if (tcp_conn->tc_snd_cwnd == tcp_conn->tc_SND_UNA)
0058772 tcp_conn->tc_snd_cwnd++;
0058773 if (tcp_Gmod4G(tcp_conn->tc_snd_cwnd, tcp_conn->tc_SND_UNA + mss2))
0058774 {
0058775 tcp_conn->tc_snd_cwnd= tcp_conn->tc_SND_UNA + mss2;
0058776 if (tcp_Gmod4G(tcp_conn->tc_SND_TRM, tcp_conn->tc_snd_cwnd))
0058777 tcp_conn->tc_SND_TRM= tcp_conn->tc_snd_cwnd;
0058778
0058779 tcp_conn->tc_snd_cthresh /= 2;
0058780 if (tcp_conn->tc_snd_cthresh < mss2)
0058781 tcp_conn->tc_snd_cthresh= mss2;
0058782 }
0058783
0058784 stt= tcp_conn->tc_stt;
0058785 assert(stt <= curr_time);
0058786 if (curr_time-stt > tcp_conn->tc_rt_dead)
0058787 {
0058788 tcp_close_connection(tcp_conn, ETIMEDOUT);
0058789 return;
0058790 }
0058791
0058792 timeout= (curr_time-stt) >> 3;
0058793 if (timeout < tcp_conn->tc_rtt)
0058794 timeout= tcp_conn->tc_rtt;
0058795 timeout += curr_time;
0058796
0058797 DBLOCK(0x20, printf(
0058798 "tcp_send_timeout: conn[%d] setting timer to %ld ms (+%ld ms)\n",
0058799 tcp_conn-tcp_conn_table, timeout*1000/HZ,
0058800 (timeout-curr_time)*1000/HZ));
0058801
0058802 clck_timer(&tcp_conn->tc_transmit_timer, timeout,
0058803 tcp_send_timeout, tcp_conn-tcp_conn_table);
clck_timer()
clck_timer(timer, timeout, func, fd) configures timer, clck_timer()'s first parameter, based on timeout, func, and fd (clck_timer()'s second, third, and fourth parameters) and places the timer in its appropriate position in timer_chain. In other words, if the new timer is scheduled to expire before any other timer, it is inserted at the head of the linked list and if the new timer is scheduled to expire after all the other timers, it is inserted at the tail of the linked list. If the new timer is scheduled to expire before any other timers, clck_timer() calls set_timer(), which sends a message to the clock task with a revised expiration time for the synchronous alarm.
An example of a client calling clck_timer() to insert a timer in timer_chain is the arp client.
0058804
0058805 if (tcp_conn->tc_rt_seq == 0)
0058806 {
0058807 tcp_conn->tc_rt_time= curr_time-tcp_conn->tc_rtt;
0058808 tcp_conn->tc_rt_seq= tcp_conn->tc_SND_UNA;
0058809 }
0058810
0058811 tcp_conn_write(tcp_conn, 0);
0058812 }
0058813
0058814
0058815 PUBLIC void tcp_fd_write(tcp_conn)
0058816 tcp_conn_t *tcp_conn;
0058817 {
0058818 tcp_fd_t *tcp_fd;
0058819 int urg, nourg, push;
0058820 u32_t max_seq;
0058821 size_t max_count, max_trans, write_count, send_count;
0058822 acc_t *data, *tmp_acc, *send_data;
0058823
0058824 assert(tcp_conn->tc_busy);
0058825 tcp_fd= tcp_conn->tc_fd;
0058826
0058827 if ((tcp_fd->tf_flags & TFF_IOCTL_IP) &&
0058828 !(tcp_fd->tf_flags & TFF_WRITE_IP))
0058829 {
0058830 if (tcp_fd->tf_ioreq != NWIOTCPSHUTDOWN)
0058831 return;
0058832 DBLOCK(0x10, printf("NWIOTCPSHUTDOWN\n"));
0058833 if (tcp_conn->tc_state == TCS_CLOSED)
0058834 {
0058835 tcp_reply_ioctl (tcp_fd, tcp_conn->tc_error);
0058836 return;
0058837 }
0058838 if (!(tcp_conn->tc_flags & TCF_FIN_SENT))
0058839 {
0058840 DBLOCK(0x10, printf("calling tcp_shutdown\n"));
0058841 tcp_shutdown (tcp_conn);
0058842 }
0058843 else
0058844 {
0058845 if (tcp_conn->tc_SND_UNA == tcp_conn->tc_SND_NXT)
0058846 {
0058847 tcp_reply_ioctl (tcp_fd, NW_OK);
0058848 DBLOCK(0x10, printf("shutdown completed\n"));
0058849 }
0058850 else
0058851 {
0058852 DBLOCK(0x10,
0058853 printf("shutdown still inprogress\n"));
0058854 }
0058855 }
0058856 return;
0058857 }
0058858
0058859 assert (tcp_fd->tf_flags & TFF_WRITE_IP);
0058860 if (tcp_conn->tc_state == TCS_CLOSED)
0058861 {
0058862 if (tcp_fd->tf_write_offset)
0058863 {
0058864 tcp_reply_write(tcp_fd,
0058865 tcp_fd->tf_write_offset);
0058866 }
0058867 else
0058868 tcp_reply_write(tcp_fd, tcp_conn->tc_error);
0058869 return;
0058870 }
0058871
0058872 urg= (tcp_fd->tf_flags & TFF_WR_URG);
0058873 push= (tcp_fd->tf_flags & TFF_PUSH_DATA);
0058874
0058875 max_seq= tcp_conn->tc_SND_UNA + tcp_conn->tc_snd_wnd;
0058876 if (urg)
0058877 max_seq++;
0058878 max_count= max_seq - tcp_conn->tc_SND_UNA;
0058879 max_trans= max_seq - tcp_conn->tc_SND_NXT;
0058880 if (tcp_fd->tf_write_count <= max_trans)
0058881 write_count= tcp_fd->tf_write_count;
0058882 else
0058883 write_count= max_trans;
0058884 if (write_count)
0058885 {
0058886 if (tcp_conn->tc_flags & TCF_BSD_URG)
0058887 {
0058888 if (tcp_Gmod4G(tcp_conn->tc_SND_NXT,
0058889 tcp_conn->tc_SND_UNA))
0058890 {
0058891 nourg= tcp_LEmod4G(tcp_conn->tc_SND_UP,
0058892 tcp_conn->tc_SND_UNA);
0058893 if ((urg && nourg) || (!urg && !nourg))
0058894 {
0058895 DBLOCK(0x20,
0058896 printf("not sending\n"));
0058897 return;
0058898 }
0058899 }
0058900 }
0058901 data= (*tcp_fd->tf_get_userdata)
0058902 (tcp_fd->tf_srfd, tcp_fd->tf_write_offset,
0058903 write_count, FALSE);
0058904
0058905 if (!data)
0058906 {
0058907 if (tcp_fd->tf_write_offset)
0058908 {
0058909 tcp_reply_write(tcp_fd,
0058910 tcp_fd->tf_write_offset);
0058911 }
0058912 else
0058913 tcp_reply_write(tcp_fd, EFAULT);
0058914 return;
0058915 }
0058916 tcp_fd->tf_write_offset += write_count;
0058917 tcp_fd->tf_write_count -= write_count;
0058918
0058919 send_data= tcp_conn->tc_send_data;
0058920 tcp_conn->tc_send_data= 0;
0058921 send_data= bf_append(send_data, data);
bf_append()
bf_append() appends one accessor linked list to another accessor linked list. For example, if the payload of an ethernet packet (1500 bytes) is appended to an ethernet header (14 bytes):
the resulting linked list is as follows:
0058922 tcp_conn->tc_send_data= send_data;
0058923 tcp_conn->tc_SND_NXT += write_count;
0058924 if (urg)
0058925 {
0058926 if (tcp_conn->tc_flags & TCF_BSD_URG)
0058927 tcp_conn->tc_SND_UP= tcp_conn->tc_SND_NXT;
0058928 else
0058929 tcp_conn->tc_SND_UP= tcp_conn->tc_SND_NXT-1;
0058930 }
0058931 if (push && !tcp_fd->tf_write_count)
0058932 tcp_conn->tc_SND_PSH= tcp_conn->tc_SND_NXT;
0058933 }
0058934 if (!tcp_fd->tf_write_count)
0058935 {
0058936 tcp_reply_write(tcp_fd, tcp_fd->tf_write_offset);
0058937 }
0058938 }
0058939
0058940 /*
0058941 tcp_shutdown
0058942 */
0058943
0058944 PUBLIC void tcp_shutdown(tcp_conn)
0058945 tcp_conn_t *tcp_conn;
0058946 {
0058947 switch (tcp_conn->tc_state)
0058948 {
0058949 case TCS_CLOSED:
0058950 case TCS_LISTEN:
0058951 case TCS_SYN_SENT:
0058952 case TCS_SYN_RECEIVED:
0058953 tcp_close_connection(tcp_conn, ENOTCONN);
0058954 return;
0058955 }
0058956
0058957 if (tcp_conn->tc_flags & TCF_FIN_SENT)
0058958 return;
0058959 tcp_conn->tc_flags |= TCF_FIN_SENT;
0058960 tcp_conn->tc_SND_NXT++;
0058961
0058962 assert (tcp_check_conn(tcp_conn) ||
0058963 (tcp_print_conn(tcp_conn), printf("\n"), 0));
0058964
0058965 tcp_conn_write(tcp_conn, 1);
0058966
0058967 /* Start the timer (if necessary) */
0058968 tcp_set_send_timer(tcp_conn);
0058969 }
0058970
0058971 PUBLIC void tcp_set_send_timer(tcp_conn)
0058972 tcp_conn_t *tcp_conn;
0058973 {
0058974 time_t curr_time;
0058975
0058976 assert(tcp_conn->tc_state != TCS_CLOSED);
0058977 assert(tcp_conn->tc_state != TCS_LISTEN);
0058978
0058979 curr_time= get_time();
get_time()
get_time() returns the number of clock ticks since reboot.
Several of the clients (eth, arp, ip, tcp, and udp) use get_time() to determine an appropriate timeout value for a given operation. For example, the arp code calls get_time() to determine an appropriate amount of time to wait for a response from an arp request before giving up.
0058980
0058981 /* Start the timer */
0058982
0058983 DBLOCK(0x20, printf(
0058984 "tcp_set_send_timer: conn[%d] setting timer to %ld ms (+%ld ms)\n",
0058985 tcp_conn-tcp_conn_table,
0058986 (curr_time+tcp_conn->tc_rtt)*1000/HZ,
0058987 tcp_conn->tc_rtt*1000/HZ));
0058988
0058989 clck_timer(&tcp_conn->tc_transmit_timer,
0058990 curr_time+tcp_conn->tc_rtt,
0058991 tcp_send_timeout, tcp_conn-tcp_conn_table);
0058992 tcp_conn->tc_stt= curr_time;
clck_timer()
clck_timer(timer, timeout, func, fd) configures timer, clck_timer()'s first parameter, based on timeout, func, and fd (clck_timer()'s second, third, and fourth parameters) and places the timer in its appropriate position in timer_chain. In other words, if the new timer is scheduled to expire before any other timer, it is inserted at the head of the linked list and if the new timer is scheduled to expire after all the other timers, it is inserted at the tail of the linked list. If the new timer is scheduled to expire before any other timers, clck_timer() calls set_timer(), which sends a message to the clock task with a revised expiration time for the synchronous alarm.
An example of a client calling clck_timer() to insert a timer in timer_chain is the arp client.
0058993
0058994 tcp_conn->tc_stt= curr_time;
0058995 }
0058996
0058997 /*
0058998 tcp_close_connection
0058999
0059000 */
0059001
0059002 PUBLIC void tcp_close_connection(tcp_conn, error)
0059003 tcp_conn_t *tcp_conn;
0059004 int error;
0059005 {
0059006 tcp_port_t *tcp_port;
0059007 tcp_fd_t *tcp_fd;
0059008 tcp_conn_t *tc;
0059009
0059010 assert (tcp_check_conn(tcp_conn));
0059011 assert (tcp_conn->tc_flags & TCF_INUSE);
0059012
0059013 tcp_conn->tc_error= error;
0059014 tcp_port= tcp_conn->tc_port;
0059015 tcp_fd= tcp_conn->tc_fd;
0059016 if (tcp_conn->tc_state == TCS_CLOSED)
0059017 return;
0059018
0059019 tcp_conn->tc_state= TCS_CLOSED;
0059020 DBLOCK(0x10, tcp_print_state(tcp_conn); printf("\n"));
0059021
0059022 if (tcp_fd)
0059023 {
0059024 tcp_conn->tc_busy++;
0059025 assert(tcp_fd->tf_conn == tcp_conn);
0059026
0059027 if (tcp_fd->tf_flags & TFF_READ_IP)
0059028 tcp_fd_read (tcp_conn, 1);
0059029 assert (!(tcp_fd->tf_flags & TFF_READ_IP));
0059030
0059031 if (tcp_fd->tf_flags & TFF_WRITE_IP)
0059032 {
0059033 tcp_fd_write(tcp_conn);
0059034 tcp_conn_write(tcp_conn, 1);
0059035 }
0059036 assert (!(tcp_fd->tf_flags & TFF_WRITE_IP));
0059037 if (tcp_fd->tf_flags & TFF_IOCTL_IP)
0059038 {
0059039 tcp_fd_write(tcp_conn);
0059040 tcp_conn_write(tcp_conn, 1);
0059041 }
0059042 if (tcp_fd->tf_flags & TFF_IOCTL_IP)
0059043 assert(tcp_fd->tf_ioreq != NWIOTCPSHUTDOWN);
0059044
0059045 if (tcp_conn->tc_connInprogress)
0059046 tcp_restart_connect(tcp_conn->tc_fd);
0059047 assert (!tcp_conn->tc_connInprogress);
0059048 assert (!(tcp_fd->tf_flags & TFF_IOCTL_IP) ||
0059049 (printf("req= 0x%lx\n", tcp_fd->tf_ioreq), 0));
0059050 tcp_conn->tc_busy--;
0059051 }
0059052
0059053 if (tcp_conn->tc_rcvd_data)
0059054 {
0059055 bf_afree(tcp_conn->tc_rcvd_data);
bf_afree()
After a chain of accessors is no longer needed, the chain (and not simply the single accessor passed as the parameter) can be freed by calling bf_free(). However, if either acc_linkC or buf_linkC of one of the accessors in the linked list is not equal to one (1), the entire chain will not be freed. For example, if buf_afree(acc1) is called for the following chain:
Then the resulting chain will be:
bf_afree() returns acc1 (accessors[63]) to acc_freelist (recall that acc_freelist is the linked list of acc_t's without an associated buffer). However, buffers512[127] cannot be freed because acc2 (accessors[64]) still references it.
bf_afree() is called after an accessor's associated data is no longer needed (for example, after a packet has been sent off by the ethernet driver).
0059056 tcp_conn->tc_rcvd_data= NULL;
0059057 }
0059058 tcp_conn->tc_flags &= ~TCF_FIN_RECV;
0059059 tcp_conn->tc_RCV_LO= tcp_conn->tc_RCV_NXT;
0059060
0059061 if (tcp_conn->tc_adv_data)
0059062 {
0059063 bf_afree(tcp_conn->tc_adv_data);
bf_afree()
After a chain of accessors is no longer needed, the chain (and not simply the single accessor passed as the parameter) can be freed by calling bf_free(). However, if either acc_linkC or buf_linkC of one of the accessors in the linked list is not equal to one (1), the entire chain will not be freed. For example, if buf_afree(acc1) is called for the following chain:
Then the resulting chain will be:
bf_afree() returns acc1 (accessors[63]) to acc_freelist (recall that acc_freelist is the linked list of acc_t's without an associated buffer). However, buffers512[127] cannot be freed because acc2 (accessors[64]) still references it.
bf_afree() is called after an accessor's associated data is no longer needed (for example, after a packet has been sent off by the ethernet driver).
0059064 tcp_conn->tc_adv_data= NULL;
0059065 }
0059066
0059067 if (tcp_conn->tc_send_data)
0059068 {
0059069 bf_afree(tcp_conn->tc_send_data);
0059070 tcp_conn->tc_send_data= NULL;
0059071 tcp_conn->tc_SND_TRM=
0059072 tcp_conn->tc_SND_NXT= tcp_conn->tc_SND_UNA;
0059073 }
0059074 tcp_conn->tc_SND_TRM= tcp_conn->tc_SND_NXT= tcp_conn->tc_SND_UNA;
0059075
0059076 if (tcp_conn->tc_remipopt)
0059077 {
0059078 bf_afree(tcp_conn->tc_remipopt);
bf_afree()
After a chain of accessors is no longer needed, the chain (and not simply the single accessor passed as the parameter) can be freed by calling bf_free(). However, if either acc_linkC or buf_linkC of one of the accessors in the linked list is not equal to one (1), the entire chain will not be freed. For example, if buf_afree(acc1) is called for the following chain:
Then the resulting chain will be:
bf_afree() returns acc1 (accessors[63]) to acc_freelist (recall that acc_freelist is the linked list of acc_t's without an associated buffer). However, buffers512[127] cannot be freed because acc2 (accessors[64]) still references it.
bf_afree() is called after an accessor's associated data is no longer needed (for example, after a packet has been sent off by the ethernet driver).
0059079 tcp_conn->tc_remipopt= NULL;
0059080 }
0059081
0059082 if (tcp_conn->tc_tcpopt)
0059083 {
0059084 bf_afree(tcp_conn->tc_tcpopt);
bf_afree()
After a chain of accessors is no longer needed, the chain (and not simply the single accessor passed as the parameter) can be freed by calling bf_free(). However, if either acc_linkC or buf_linkC of one of the accessors in the linked list is not equal to one (1), the entire chain will not be freed. For example, if buf_afree(acc1) is called for the following chain:
Then the resulting chain will be:
bf_afree() returns acc1 (accessors[63]) to acc_freelist (recall that acc_freelist is the linked list of acc_t's without an associated buffer). However, buffers512[127] cannot be freed because acc2 (accessors[64]) still references it.
bf_afree() is called after an accessor's associated data is no longer needed (for example, after a packet has been sent off by the ethernet driver).
0059085 tcp_conn->tc_tcpopt= NULL;
0059086 }
0059087
0059088 if (tcp_conn->tc_frag2send)
0059089 {
0059090 bf_afree(tcp_conn->tc_frag2send);
bf_afree()
After a chain of accessors is no longer needed, the chain (and not simply the single accessor passed as the parameter) can be freed by calling bf_free(). However, if either acc_linkC or buf_linkC of one of the accessors in the linked list is not equal to one (1), the entire chain will not be freed. For example, if buf_afree(acc1) is called for the following chain:
Then the resulting chain will be:
bf_afree() returns acc1 (accessors[63]) to acc_freelist (recall that acc_freelist is the linked list of acc_t's without an associated buffer). However, buffers512[127] cannot be freed because acc2 (accessors[64]) still references it.
bf_afree() is called after an accessor's associated data is no longer needed (for example, after a packet has been sent off by the ethernet driver).
0059091 tcp_conn->tc_frag2send= NULL;
0059092 }
0059093 if (tcp_conn->tc_flags & TCF_MORE2WRITE)
0059094 {
0059095 for (tc= tcp_port->tp_snd_head; tc; tc= tc->tc_send_link)
0059096 {
0059097 if (tc->tc_send_link == tcp_conn)
0059098 break;
0059099 }
0059100 if (tc == NULL)
0059101 {
0059102 assert(tcp_port->tp_snd_head == tcp_conn);
0059103 tcp_port->tp_snd_head= tcp_conn->tc_send_link;
0059104 }
0059105 else
0059106 {
0059107 tc->tc_send_link= tcp_conn->tc_send_link;
0059108 if (tc->tc_send_link == NULL)
0059109 tcp_port->tp_snd_tail= tc;
0059110 }
0059111 tcp_conn->tc_flags &= ~TCF_MORE2WRITE;
0059112 }
0059113
0059114 clck_untimer (&tcp_conn->tc_transmit_timer);
clck_untimer()
clck_untimer(timer) releases timer, clck_untimer()'s only parameter. If necessary, clck_untimer() sends a message to the clock task requesting a synchronous alarm with a revised expiration time (actually, clck_untimer() calls set_timer(), which would send the message). Remember that the synchronous alarm task is aware of only a single alarm at any given time.
0059115 tcp_conn->tc_transmit_seq= 0;
0059116
0059117 /* clear all flags but TCF_INUSE */
0059118 tcp_conn->tc_flags &= TCF_INUSE;
0059119 assert (tcp_check_conn(tcp_conn));
0059120 }
0059121
0059122 /*
0059123 * $PchId: tcp_send.c,v 1.12 1996/12/17 07:57:11 philip Exp $
0059124 */