3 International Computer Science Institute
6 This file may contain software code originally developed for the
7 Sting project. The Sting software carries the following copyright:
9 Copyright (c) 1998, 1999
10 Stefan Savage and the University of Washington.
13 Redistribution and use in source and binary forms, with or without
14 modification, are permitted provided that the following conditions
16 1. Redistributions of source code must retain the above copyright
17 notice, this list of conditions and the following disclaimer.
18 2. Redistributions in binary form must reproduce the above copyright
19 notice, this list of conditions and the following disclaimer in the
20 documentation and/or other materials provided with the distribution.
21 3. All advertising materials mentioning features or use of this software
22 must display the following acknowledgment:
23 This product includes software developed by ACIRI, the AT&T
24 Center for Internet Research at ICSI (the International Computer
25 Science Institute). This product may also include software developed
26 by Stefan Savage at the University of Washington.
27 4. The names of ACIRI, ICSI, Stefan Savage and University of Washington
28 may not be used to endorse or promote products derived from this software
29 without specific prior written permission.
31 THIS SOFTWARE IS PROVIDED BY ICSI AND CONTRIBUTORS ``AS IS'' AND
32 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
33 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
34 ARE DISCLAIMED. IN NO EVENT SHALL ICSI OR CONTRIBUTORS BE LIABLE
35 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
36 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
37 OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
38 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
39 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
40 OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
43 #include <sys/types.h>
44 #include <sys/param.h>
58 struct TcpSession session
;
60 int EstablishSession(uint32 sourceAddress
,
64 int ip_optlen
, // AM: add support for IP options
65 char *ip_opt
, // AM: add support for IP options
72 uint8 tcp_flags
) // AM: Add a tcp_flags parameter
77 struct IPPacket
*p
= NULL
;
78 struct IPPacket
*synPacket
;
80 struct pcap_pkthdr pi
;
81 int synAckReceived
= 0;
82 int numRetransmits
= 0;
87 if (session
.debug
>= SESSION_DEBUG_HIGH
) {
88 printf("In EstablishSession...\n");
93 session
.src
= sourceAddress
;
94 session
.sport
= sourcePort
;
95 session
.dst
= targetAddress
;
96 session
.dport
= targetPort
;
97 session
.rcv_wnd
= maxwin
* mss
;
98 session
.snd_nxt
= arc4random(); /* random initial sequence number */
99 session
.iss
= session
.snd_nxt
;
103 session
.maxseqseen
= 0 ;
104 session
.epochTime
= GetTime();
105 session
.maxpkts
= maxpkts
;
106 session
.num_unwanted_drops
= 0;
107 session
.num_reordered
= 0;
108 session
.num_rtos
= 0;
109 session
.num_dup_acks
= 0;
110 session
.num_pkts_0_dup_acks
= 0;
111 session
.num_pkts_1_dup_acks
= 0;
112 session
.num_pkts_2_dup_acks
= 0;
113 session
.num_pkts_3_dup_acks
= 0;
114 session
.num_pkts_4_or_more_dup_acks
= 0;
115 session
.num_dupack_ret
= 0;
116 session
.num_reord_ret
= 0;
117 session
.num_reordered
= 0;
118 session
.num_dup_transmissions
= 0;
119 session
.ignore_result
= 0;
120 session
.curr_ttl
= 0;
122 if ((session
.mtu
< 1) || (session
.mtu
> 1460)) {
126 if (session
.verbose
) {
127 printf("session.MTU = %d\n", session
.mtu
);
130 if ((session
.dataRcvd
= (uint8
*)calloc(sizeof(uint8
), mss
* session
.maxpkts
)) == NULL
) {
131 perror("ERROR: no memmory to store data:\n");
132 printf("RETURN CODE: %d\n", ERR_MEM_ALLOC
);
136 /* Now open a raw socket for sending our "fake" TCP segments */
137 if ((rawSocket
= socket(AF_INET
, SOCK_RAW
, IPPROTO_RAW
)) < 0) {
138 perror("ERROR: couldn't open socket:");
139 printf("RETURN CODE: %d\n", ERR_SOCKET_OPEN
);
140 Quit(ERR_SOCKET_OPEN
);
143 if (setsockopt(rawSocket
, IPPROTO_IP
, IP_HDRINCL
, (char *)&flag
,sizeof(flag
)) < 0) {
144 perror("ERROR: couldn't set raw socket options:");
145 printf("RETURN CODE: %d\n", ERR_SOCKOPT
);
149 session
.socket
= rawSocket
;
151 /* Allocate SYN packet */
152 synPacket
= AllocateIPPacket(ip_optlen
, optlen
, 0, "EstablishSession (SYN)");
154 /* Copy IP options at the end of IpHeader structure - New */
156 memcpy((char *)synPacket
->ip
+ sizeof(struct IpHeader
), ip_opt
, ip_optlen
);
159 /* Copy TCP options at the end of TcpHeader structure - New */
161 memcpy((char *)synPacket
->tcp
+ sizeof(struct TcpHeader
), opt
, optlen
);
165 SendSessionPacket(synPacket
,
166 sizeof(struct IpHeader
) + ip_optlen
+ sizeof(struct TcpHeader
) + optlen
,
167 TCPFLAGS_SYN
| tcp_flags
,
168 ip_optlen
, /* IP opt len */
169 optlen
, /* TCP opt len */
172 timeoutTime
= GetTime() + SYNTIMEOUT
;
175 * Wait for SYN/ACK and retransmit SYN if appropriate
176 * not great, but it gets the job done
179 while(!synAckReceived
&& numRetransmits
< MAXSYNRETRANSMITS
) {
181 while(GetTime() < timeoutTime
) {
183 /* Have we captured any packets? */
185 if ((read_packet
= (char *)CaptureGetPacket(&pi
)) != NULL
) {
187 p
= (struct IPPacket
*)FindHeaderBoundaries(read_packet
);
189 /* Received a packet from us to them */
190 if (INSESSION(p
, session
.src
, session
.sport
, session
.dst
, session
.dport
)) {
193 if (p
->tcp
->tcp_flags
& TCPFLAGS_SYN
) {
195 if (session
.debug
>= SESSION_DEBUG_LOW
) {
202 ts1
= pi
.ts
.tv_sec
+ (double)pi
.ts
.tv_usec
/1000000.0;
203 session
.totSeenSent
++ ;
213 if (INSESSION(p
, session
.dst
, session
.dport
, session
.src
, session
.sport
)) {
215 /* Is it a SYN/ACK? */
216 if (p
->tcp
->tcp_flags
& (TCPFLAGS_SYN
| TCPFLAGS_ACK
)) {
218 timeoutTime
= GetTime(); /* force exit */
220 ts2
= pi
.ts
.tv_sec
+ (double)pi
.ts
.tv_usec
/1000000.0;
221 session
.rtt
= ts2
- ts1
;
223 if (numRetransmits
> 0) {
224 session
.rtt_unreliable
= 1;
225 printf("##### Unreliable\n"); /* ACK for which SYN? */
228 if (session
.debug
>= SESSION_DEBUG_LOW
) {
231 printf("Connection setup took %d ms\n",(int)((ts2
- ts1
) * 1000.0));
236 /* Save ttl for,admittedly poor,indications of reverse route change */
237 session
.ttl
= p
->ip
->ip_ttl
;
238 session
.snd_wnd
= ntohl(p
->tcp
->tcp_win
);
256 if (!synAckReceived
) {
258 if (session
.debug
>= SESSION_DEBUG_LOW
) {
259 printf("SYN timeout. Retransmitting\n");
262 SendSessionPacket(synPacket
,
263 sizeof(struct IpHeader
) + ip_optlen
+ sizeof(struct TcpHeader
) + optlen
,
264 TCPFLAGS_SYN
| tcp_flags
,
265 ip_optlen
, /* IP opt len */
266 optlen
, /* TCP opt len */
269 timeoutTime
= GetTime() + SYNTIMEOUT
;
274 if (numRetransmits
>= MAXSYNRETRANSMITS
) {
275 printf("ERROR: Could not establish contact after %d retries\nRETURN CODE: %d\n",
276 numRetransmits
, NO_CONNECTION
);
280 /* Update session variables */
281 session
.irs
= ntohl(p
->tcp
->tcp_seq
);
282 session
.dataRcvd
[0] = 1 ;
283 session
.rcv_nxt
= session
.irs
+ 1; /* SYN/ACK takes up a byte of seq space */
284 session
.snd_nxt
= session
.iss
+ 1; /* SYN takes up a byte of seq space */
285 session
.snd_una
= session
.iss
+ 1;
286 session
.maxseqseen
= ntohl(p
->tcp
->tcp_seq
);
288 session
.initSession
= 1;
289 if (session
.debug
>= SESSION_DEBUG_LOW
) {
290 printf("src = %s:%d (%u)\n", InetAddress(session
.src
), session
.sport
, session
.iss
);
291 printf("dst = %s:%d (%u)\n", InetAddress(session
.dst
), session
.dport
, session
.irs
);
295 free(synPacket
->tcp
);
298 if (session
.debug
>= SESSION_DEBUG_HIGH
) {
299 printf("Out of EstablishSession...\n");
302 session
.start_time
= GetTime();
308 int PrepareRequest(char *data
, char *filename
)
312 char h2
[] = " HTTP/1.1";
313 char h3
[] = "Host: ";
314 char h4
[] = "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11; DigExt; TBIT)";
315 char h5
[] = "Accept: */*";
318 char h7
[] = "Pragma: no-cache";
319 char h8
[] = "Cache-control: no-chache";
320 char deffile
[] = DEFAULT_FILENAME
;
323 if (session
.debug
>= SESSION_DEBUG_HIGH
) {
324 printf("In PrepareRequest...\n");
327 if (filename
== NULL
) {
332 if (strlen(session
.targetName
) > 0) {
336 "%s/%s %s\r\n%s\r\n%s\r\n%s\r\n%s\r\n%s%s\r\n\r\n",
349 "%s%s%s\r\n%s\r\n\r\n",
356 if (session
.debug
>= SESSION_DEBUG_HIGH
) {
357 printf("Out PrepareRequest...\n");
360 return ((int)strlen(data
));
365 void SendRequest(char *filename
, void (*ackData
)(struct IPPacket
*p
))
368 struct IPPacket
*p
, *datapkt
;
369 struct pcap_pkthdr pi
;
373 double startTime
= 0;
375 char data
[MAXREQUESTLEN
];
379 if (session
.debug
>= SESSION_DEBUG_HIGH
) {
380 printf("In SendRequest...\n");
383 datalen
= PrepareRequest(data
, filename
);
385 ipsz
= sizeof(struct IpHeader
) + sizeof(struct TcpHeader
) + datalen
+ 1;
387 /* Allocate space for IP data packet */
388 datapkt
= AllocateIPPacket(0, 0, datalen
+ 1, "SendRequest (Data)");
390 dataptr
= (char *)datapkt
->tcp
+ sizeof(struct TcpHeader
);
391 memcpy((void *)dataptr
, (void *)data
, datalen
);
393 /* Send the data packet. Try to "achieve" reliability by sending the
394 * packet upto 5 times, wating for 2 seconds between packets (BAD
403 SendSessionPacket(datapkt
,
405 TCPFLAGS_PSH
| TCPFLAGS_ACK
,
410 startTime
= GetTime();
416 /* Check if we have received any packets */
417 if ((read_packet
= (char *)CaptureGetPacket(&pi
)) != NULL
) {
419 p
= (struct IPPacket
*)FindHeaderBoundaries(read_packet
);
422 * packet that we sent?
425 if (INSESSION(p
,session
.src
,session
.sport
,session
.dst
,session
.dport
) &&
426 (p
->tcp
->tcp_flags
== (TCPFLAGS_PSH
| TCPFLAGS_ACK
)) &&
427 (ntohl(p
->tcp
->tcp_seq
) == session
.snd_nxt
) &&
428 (ntohl(p
->tcp
->tcp_ack
) <= session
.rcv_nxt
)) {
430 if (session
.debug
>= SESSION_DEBUG_LOW
) {
431 printf("xmit %d\n", i
);
439 //session.snd_nxt += datalen + 1;
440 session
.totSeenSent
++;
448 if (INSESSION(p
,session
.dst
,session
.dport
,session
.src
,session
.sport
) &&
449 (p
->tcp
->tcp_flags
& TCPFLAGS_ACK
) &&
450 (ntohl(p
->tcp
->tcp_seq
) == session
.rcv_nxt
) &&
451 (ntohl(p
->tcp
->tcp_ack
) >= session
.snd_una
)) {
454 session
.snd_una
= ntohl(p
->tcp
->tcp_ack
);
456 if (p
->ip
->ip_ttl
!= session
.ttl
) {
457 printf("#### WARNING: route may have changed (ttl was %d, is %d).\n",
458 session
.ttl
, p
->ip
->ip_ttl
);
459 session
.ttl
= p
->ip
->ip_ttl
;
462 if (session
.debug
>= SESSION_DEBUG_LOW
) {
463 printf("rcvd %d\n", i
);
469 session
.snd_nxt
+= datalen
+ 1;
471 /* if the packet also contains data, receive it and send an ack if needed */
483 if ((GetTime() - startTime
>= REXMITDELAY
) &&
484 (sendflag
== 0) && (i
< MAXDATARETRANSMITS
)) {
488 if (i
>= MAXDATARETRANSMITS
) {
489 printf ("ERROR: sent request 5 times without response\nRETURN CODE: %d\n",
490 SEND_REQUEST_FAILED
);
491 Quit(SEND_REQUEST_FAILED
);
500 if (session
.debug
>= SESSION_DEBUG_HIGH
) {
501 printf("Out of SendRequest...\n");
505 void SendSessionPacket(struct IPPacket
*p
,
506 uint16 ip_len
, uint8 tcp_flags
, uint16 ip_optlen
, uint16 optlen
,
509 if (session
.debug
>= SESSION_DEBUG_HIGH
) {
510 printf("In SendSessionPacket...\n");
513 session
.src
, session
.dst
, session
.sport
, session
.dport
,
514 session
.snd_nxt
, session
.rcv_nxt
, tcp_flags
,
516 (ip_len
- sizeof(struct IpHeader
) - ip_optlen
- sizeof(struct TcpHeader
) - optlen
),
517 ip_optlen
, optlen
, iptos
, 0);
520 /* Store packet here rather than in rcvData() because otherwise some
521 * ACKs may not be accounted for upon receiving reordered packets */
526 ip_len
, /* Total IP datagram size */
527 ip_optlen
, /* ip options len */
528 optlen
); /* tcp options len */
530 if (session
.debug
>= SESSION_DEBUG_HIGH
) {
531 printf("Out of SendSessionPacket...\n");
537 void SendICMPReply(struct IPPacket
*p
)
540 struct ICMPUnreachableErrorPacket
*icmp_pkt
;
543 struct IpHeader
*ip
= p
->ip
;
544 struct TcpHeader
*tcp
= p
->tcp
;
546 if (session
.debug
>= SESSION_DEBUG_HIGH
) {
547 printf("In SendICMPReply...\n");
550 icmpsz
= sizeof(struct ICMPUnreachableErrorPacket
);
551 if ((icmp_pkt
= (struct ICMPUnreachableErrorPacket
*)calloc(icmpsz
+ 1, 1)) == NULL
) {
552 perror("ERROR: no space for ICMP packet:");
553 Quit(ERR_MEM_ALLOC
) ;
556 /* Fill IP Header of ICMP packet */
557 bzero((char *)icmp_pkt
, sizeof(struct ICMPUnreachableErrorPacket
));
558 icmp_pkt
->ip
.ip_src
= ip
->ip_dst
;
559 icmp_pkt
->ip
.ip_dst
= ip
->ip_src
;
560 icmp_pkt
->ip
.ip_p
= IPPROTOCOL_ICMP
;
561 icmp_pkt
->ip
.ip_xsum
=
562 htons((uint16
)(sizeof(struct IcmpHeader
) + sizeof(struct IpHeader
) + sizeof(struct IpHeader
) + 8)); /* pseudo hdr */
563 icmp_pkt
->ip
.ip_ttl
= 60;
564 icmp_pkt
->ip
.ip_tos
= 0x00;
565 icmp_pkt
->ip
.ip_vhl
= 0x45;
567 icmp_pkt
->ip
.ip_off
= IP_DF
;
568 icmp_pkt
->ip
.ip_len
= (uint16
)(sizeof(struct ICMPUnreachableErrorPacket
));
569 #else /* __FreeBSD__ */
570 icmp_pkt
->ip
.ip_off
= htons(IP_DF
);
571 icmp_pkt
->ip
.ip_len
= htons((uint16
)((sizeof (struct ICMPUnreachableErrorPacket
) + 8 + 1)));
572 #endif /* __FreeBSD__ */
574 /* Fill ICMP header */
575 icmp_pkt
->icmp
.icmp_type
= 0x3;
576 icmp_pkt
->icmp
.icmp_code
= 0x4;
577 icmp_pkt
->icmp
.icmp_xsum
= 0;
578 icmp_pkt
->icmp
.icmp_unused
= 0;
579 icmp_pkt
->icmp
.icmp_mtu
= htons(session
.mtu
);
581 /* Fill in ip header of offending packet */
582 icmp_pkt
->off_ip
.ip_src
= ip
->ip_src
;
583 icmp_pkt
->off_ip
.ip_dst
= ip
->ip_dst
;
584 icmp_pkt
->off_ip
.ip_p
= ip
->ip_p
;
585 icmp_pkt
->off_ip
.ip_xsum
= ip
->ip_xsum
;
586 icmp_pkt
->off_ip
.ip_ttl
= ip
->ip_ttl
;
587 icmp_pkt
->off_ip
.ip_tos
= ip
->ip_tos
;
588 icmp_pkt
->off_ip
.ip_vhl
= ip
->ip_vhl
;
589 icmp_pkt
->off_ip
.ip_p
= ip
->ip_p
;
591 icmp_pkt
->off_ip
.ip_off
= ntohs(ip
->ip_off
);
592 icmp_pkt
->off_ip
.ip_len
= ntohs(ip
->ip_len
);
593 #else /* __FreeBSD__ */
594 icmp_pkt
->off_ip
.ip_off
= ip
->ip_off
;
595 icmp_pkt
->off_ip
.ip_len
= ip
->ip_len
;
596 #endif /* __FreeBSD__ */
598 icmp_pkt
->tcp_sport
= tcp
->tcp_sport
;
599 icmp_pkt
->tcp_dport
= tcp
->tcp_dport
;
600 icmp_pkt
->tcp_seqno
= (uint32
)tcp
->tcp_seq
;
602 icmp_pkt
->icmp
.icmp_xsum
= InetChecksum((uint16
*)(&(icmp_pkt
->icmp
)), NULL
,
603 (uint16
)(sizeof(struct IcmpHeader
) + sizeof(struct IpHeader
) + 8), 0);
605 if (session
.verbose
) {
606 printf("++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n");
607 printf("TCP Packet: %lu\n", sizeof(struct IPPacket
));
609 printf("ICMP Packet: %lu\n", sizeof(struct ICMPUnreachableErrorPacket
));
610 PrintICMPUnreachableErrorPacket(icmp_pkt
);
611 printf("++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n");
614 SendICMPPkt(icmp_pkt
, sizeof(struct ICMPUnreachableErrorPacket
));
616 if (session
.debug
>= SESSION_DEBUG_HIGH
) {
617 printf("Out of SendICMPReply...\n");
622 void SendPkt(struct IPPacket
*p
, uint16 ip_len
, int ip_optlen
,
625 struct sockaddr_in sockAddr
;
628 if (session
.debug
>= SESSION_DEBUG_HIGH
) {
629 printf("In SendPkt...\n");
631 /* Assemble contiguos packet to be sent */
632 if ((assembled_pkt
= (char *)calloc(1, ip_len
)) == NULL
) {
633 printf("SendPkt: Cannot allocate memory for assembled packet\n");
636 /* Copy IP Header and options, if any */
637 memcpy((char *)assembled_pkt
, (char *)(p
->ip
),
638 sizeof(struct IpHeader
) + ip_optlen
);
640 /* Copy TCP Header and options, if any */
641 memcpy((char *)(assembled_pkt
+ sizeof(struct IpHeader
) + ip_optlen
),
643 sizeof(struct TcpHeader
) + tcp_optlen
);
645 /* Copy data bytes, if any */
646 datalen
= ip_len
- ((sizeof(struct IpHeader
) + ip_optlen
+ sizeof(struct TcpHeader
) + tcp_optlen
));
649 memcpy((char *)assembled_pkt
+ sizeof(struct IpHeader
) + ip_optlen
+ sizeof(struct TcpHeader
) + tcp_optlen
,
650 (char *)p
->tcp
+ sizeof(struct TcpHeader
) + tcp_optlen
, datalen
);
654 sockAddr
.sin_family
= AF_INET
;
655 sockAddr
.sin_addr
.s_addr
= session
.dst
;
657 if ((nbytes
= (int)sendto(session
.socket
,
658 (char *)assembled_pkt
,
661 (struct sockaddr
*)&sockAddr
,
662 sizeof(sockAddr
))) < ip_len
) {
663 printf("#### WARNING: only sent %d of %d bytes\n", nbytes
, ip_len
);
672 if (session
.debug
>= SESSION_DEBUG_HIGH
) {
673 printf("Out SendPkt...\n");
680 void SendICMPPkt(struct ICMPUnreachableErrorPacket
*p
, uint16 len
) {
683 struct sockaddr_in sockAddr
;
685 sockAddr
.sin_family
= AF_INET
;
686 sockAddr
.sin_addr
.s_addr
= session
.dst
;
688 nbytes
= sendto(session
.socket
, (char *)p
, len
, 0,
689 (struct sockaddr
*)&sockAddr
,
693 printf("#### WARNING: only sent %zd of %d (errno: %d) bytes\n",
702 void rcvData (void (*ackData
)(struct IPPacket
*p
))
705 struct pcap_pkthdr pi
;
708 double startTime
= GetTime () ;
710 if (session
.debug
>= SESSION_DEBUG_HIGH
) {
711 printf("In rcvData...\n");
716 if ((GetTime() - startTime
) > (MAXDATARETRANSMITS
* REXMITDELAY
)) {
717 printf ("ERROR: no Data received for %f seconds\nRETURN CODE: %d\n",
718 (MAXDATARETRANSMITS
*REXMITDELAY
), NO_DATA_RCVD
);
722 if ((read_packet
= (char *)CaptureGetPacket(&pi
)) != NULL
) {
724 p
= (struct IPPacket
*)FindHeaderBoundaries(read_packet
);
727 * Packet that we sent?
730 if (INSESSION(p
,session
.src
,session
.sport
,session
.dst
,session
.dport
) &&
731 ((p
->tcp
->tcp_flags
& TCPFLAGS_ACK
) || (p
->tcp
->tcp_flags
& TCPFLAGS_FIN
)) &&
732 (ntohl(p
->tcp
->tcp_seq
) == session
.snd_nxt
) &&
733 (ntohl(p
->tcp
->tcp_ack
) <= session
.rcv_nxt
)) {
735 if (session
.debug
>= SESSION_DEBUG_LOW
) {
740 session
.totSeenSent
++ ;
748 * Data that we were expecting?
751 if (INSESSION(p
,session
.dst
,session
.dport
,session
.src
,session
.sport
) &&
752 (p
->tcp
->tcp_flags
& TCPFLAGS_ACK
) &&
753 (ntohl(p
->tcp
->tcp_ack
) >= session
.snd_una
)) {
755 if (p
->ip
->ip_ttl
!= session
.ttl
) {
756 printf("#### WARNING: route may have changed (ttl was %d, is %d).\n",
757 session
.ttl
, p
->ip
->ip_ttl
);
758 session
.ttl
= p
->ip
->ip_ttl
;
761 if (session
.debug
>= SESSION_DEBUG_LOW
) {
767 startTime
= GetTime () ;
770 /* if the packet also contains data, receive it, and send an ack if needed */