aboutsummaryrefslogtreecommitdiffstats
path: root/network_cmds/ecnprobe/ecn.c
diff options
context:
space:
mode:
Diffstat (limited to 'network_cmds/ecnprobe/ecn.c')
-rw-r--r--network_cmds/ecnprobe/ecn.c1119
1 files changed, 1119 insertions, 0 deletions
diff --git a/network_cmds/ecnprobe/ecn.c b/network_cmds/ecnprobe/ecn.c
new file mode 100644
index 0000000..b7dabdf
--- /dev/null
+++ b/network_cmds/ecnprobe/ecn.c
@@ -0,0 +1,1119 @@
+/*
+ Copyright (c) 2000
+ International Computer Science Institute
+ All rights reserved.
+
+ This file may contain software code originally developed for the
+ Sting project. The Sting software carries the following copyright:
+
+ Copyright (c) 1998, 1999
+ Stefan Savage and the University of Washington.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. All advertising materials mentioning features or use of this software
+ must display the following acknowledgment:
+ This product includes software developed by ACIRI, the AT&T
+ Center for Internet Research at ICSI (the International Computer
+ Science Institute). This product may also include software developed
+ by Stefan Savage at the University of Washington.
+ 4. The names of ACIRI, ICSI, Stefan Savage and University of Washington
+ may not be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY ICSI AND CONTRIBUTORS ``AS IS'' AND
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL ICSI OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ SUCH DAMAGE.
+*/
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/time.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include "base.h"
+#include "inet.h"
+#include "session.h"
+#include "capture.h"
+#include "support.h"
+#include "history.h"
+#include "ecn.h"
+
+extern struct TcpSession session;
+extern struct History history[];
+
+#define ESTABLISH_SUCCESS 0
+#define ESTABLISH_FAILURE_EARLY_RST 1
+#define ESTABLISH_FAILURE_NO_REPLY 2
+int EstablishTcpConnection(u_int8_t syn_flags, u_int8_t ip_tos)
+{
+ struct IPPacket *synPacket = NULL, *ackPacket = NULL;
+ char *read_packet;
+ struct pcap_pkthdr pi;
+ int synAckReceived = 0;
+ int numRetransmits = 0;
+ double timeoutTime;
+ int tcpoptlen = 4; /* For negotiating MSS */
+ u_int8_t *opt = NULL;
+ struct IPPacket *p = NULL;
+
+ /* allocate the syn packet -- Changed for new IPPacket structure */
+ synPacket = AllocateIPPacket(0, tcpoptlen, 0, "ECN (SYN)");
+ opt = (((u_int8_t *)synPacket->tcp) + sizeof(struct TcpHeader));
+ opt[0] = (u_int8_t)TCPOPT_MAXSEG;
+ opt[1] = (u_int8_t)TCPOLEN_MAXSEG;
+ *((u_int16_t *)((u_int8_t *)opt + 2)) = htons(session.mss);
+
+ SendSessionPacket(synPacket,
+ sizeof(struct IpHeader) + sizeof(struct TcpHeader) + tcpoptlen,
+ TCPFLAGS_SYN | syn_flags, 0, tcpoptlen, ip_tos);
+ timeoutTime = GetTime() + 1;
+
+ /*
+ * Wait for SYN/ACK and retransmit SYN if appropriate
+ * not great, but it gets the job done
+ */
+
+ while(!synAckReceived && numRetransmits < 3) {
+ while(GetTime() < timeoutTime) {
+ /* Have we captured any packets? */
+ if ((read_packet = (char *)CaptureGetPacket(&pi)) != NULL) {
+ p = (struct IPPacket *)FindHeaderBoundaries(read_packet);
+ /* Received a packet from us to them */
+ if (INSESSION(p, session.src, session.sport,
+ session.dst, session.dport)) {
+ /* Is it a SYN/ACK? */
+ if (p->tcp->tcp_flags & TCPFLAGS_SYN) {
+ if (session.debug >= SESSION_DEBUG_LOW) {
+ PrintTcpPacket(p);
+ }
+ StorePacket(p);
+ session.totSeenSent++ ;
+ } else {
+ processBadPacket(p);
+ }
+ continue;
+ }
+
+ /* Received a packet from them to us */
+ if (INSESSION(p, session.dst, session.dport, session.src,
+ session.sport)) {
+ /* Is it a SYN/ACK? */
+ if ((p->tcp->tcp_flags & TCPFLAGS_SYN) &&
+ (p->tcp->tcp_flags & TCPFLAGS_ACK)) {
+ timeoutTime = GetTime(); /* force exit */
+ synAckReceived++;
+ if (session.debug >= SESSION_DEBUG_LOW) {
+ PrintTcpPacket(p);
+ }
+ StorePacket(p);
+
+ /*
+ * Save ttl for,admittedly poor,indications of reverse
+ * route change
+ */
+ session.ttl = p->ip->ip_ttl;
+ session.snd_wnd = ntohl(p->tcp->tcp_win);
+ session.totRcvd ++;
+ break;
+ } else {
+ if ((p->tcp->tcp_flags)& (TCPFLAGS_RST)) {
+ printf ("ERROR: EARLY_RST\n");
+ return(ESTABLISH_FAILURE_EARLY_RST);
+ }
+ }
+ }
+ }
+ }
+
+ if (!synAckReceived) {
+ if (session.debug >= SESSION_DEBUG_LOW) {
+ printf("SYN timeout. Retransmitting\n");
+ }
+ SendSessionPacket(synPacket,
+ sizeof(struct IpHeader) + sizeof(struct TcpHeader) + tcpoptlen,
+ TCPFLAGS_SYN | syn_flags, 0, tcpoptlen, ip_tos);
+ timeoutTime = GetTime() + 1;
+ numRetransmits++;
+ }
+ }
+
+ if (numRetransmits >= 3) {
+ printf("ERROR: No connection after 3 retries...\nRETURN CODE: %d\n",
+ NO_CONNECTION);
+ return(ESTABLISH_FAILURE_NO_REPLY);
+ }
+ if (session.debug >= SESSION_DEBUG_LOW)
+ printf("Received SYN-ACK, try to send the third Ack\n");
+ /* Update session variables */
+ session.irs = ntohl(p->tcp->tcp_seq);
+ session.dataRcvd[0] = 1 ;
+ session.rcv_nxt = session.irs + 1; /* SYN/ACK takes up a byte of seq space */
+ session.snd_nxt = session.iss + 1; /* SYN takes up a byte of seq space */
+ session.snd_una = session.iss + 1;
+ session.maxseqseen = ntohl(p->tcp->tcp_seq);
+ session.initSession = 1;
+ if (session.debug >= SESSION_DEBUG_LOW) {
+ printf("src = %s:%d (%u)\n", InetAddress(session.src),
+ session.sport, session.iss);
+ printf("dst = %s:%d (%u)\n",InetAddress(session.dst),
+ session.dport, session.irs);
+ }
+
+ /* allocate the syn packet -- Changed for new IPPacket structure */
+ ackPacket = AllocateIPPacket(0, 0, 0, "Third ACK");
+ /* send an ACK */
+ SendSessionPacket(ackPacket,
+ sizeof(struct IpHeader) + sizeof(struct TcpHeader),
+ TCPFLAGS_ACK, 0, 0, 0);
+ FreeIPPacket(&synPacket);
+ FreeIPPacket(&ackPacket);
+ return(ESTABLISH_SUCCESS);
+}
+
+void ECNTest(u_int32_t sourceAddress, u_int16_t sourcePort,
+ u_int32_t targetAddress, u_int16_t targetPort, int mss)
+{
+ int rawSocket, rc, flag = 1;
+
+ arc4random_stir();
+
+ session.src = sourceAddress;
+ session.sport = sourcePort;
+ session.dst = targetAddress;
+ session.dport = targetPort;
+ session.rcv_wnd = 5*mss;
+ /* random initial sequence number */
+ session.snd_nxt = arc4random();
+ session.iss = session.snd_nxt;
+ session.rcv_nxt = 0;
+ session.irs = 0;
+ session.mss = mss;
+ session.maxseqseen = 0;
+ session.epochTime = GetTime ();
+ session.maxpkts = 1000;
+/* session.debug = SESSION_DEBUG_LOW; */
+ session.debug = 0;
+ if ((session.dataRcvd = (u_int8_t *)calloc(sizeof(u_int8_t),
+ mss * session.maxpkts)) == NULL) {
+ printf("no memmory to store data:\nRETURN CODE: %d",
+ ERR_MEM_ALLOC);
+ Quit(ERR_MEM_ALLOC);
+ }
+
+ if ((rawSocket = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) {
+ perror("ERROR: couldn't open socket:");
+ Quit(ERR_SOCKET_OPEN);
+ }
+
+ if (setsockopt(rawSocket, IPPROTO_IP, IP_HDRINCL,
+ (char *)&flag, sizeof(flag)) < 0) {
+ perror("ERROR: couldn't set raw socket options:");
+ Quit(ERR_SOCKOPT);
+ }
+
+ session.socket = rawSocket;
+
+ /* Establish a TCP connections with ECN bits */
+ rc = EstablishTcpConnection(TCPFLAGS_ECN_ECHO | TCPFLAGS_CWR, 0);
+ switch (rc) {
+ case ESTABLISH_FAILURE_EARLY_RST:
+ {
+ /* Received a RST when ECN bits are used. Try again without ECN */
+ rc = EstablishTcpConnection(0, 0);
+ if (rc == ESTABLISH_FAILURE_EARLY_RST) {
+ printf("Received RST with or without ECN negotiation\n");
+ printf("The server might not be listening on this port\n");
+ Quit(EARLY_RST);
+ } else if (rc == ESTABLISH_SUCCESS) {
+ printf("Received RST with ECN.\n");
+ printf("Connection established successfully without ECN\n");
+ Quit(ECN_SYN_DROP);
+ } else if (rc == ESTABLISH_FAILURE_NO_REPLY) {
+ printf("Received RST with ECN\n");
+ printf("Exceed max SYN retransmits without ECN\n");
+ Quit(NO_CONNECTION);
+ }
+ break;
+ }
+ case ESTABLISH_FAILURE_NO_REPLY:
+ {
+ /* No reply after retring, try again without ECN */
+ rc = EstablishTcpConnection(0, 0);
+ if (rc == ESTABLISH_FAILURE_EARLY_RST) {
+ printf("Exceeded max SYN retransmits with ECN\n");
+ printf("Received RST without ECN\n");
+ Quit(NO_CONNECTION);
+ } else if (rc == ESTABLISH_FAILURE_NO_REPLY) {
+ printf("Exceeded max SYN retransmits with ECN\n");
+ printf("Exceeded max SYN retransmits without ECN\n");
+ Quit(NO_CONNECTION);
+ } else {
+ printf("Exceeded max SYN retransmits with ECN\n");
+ printf("Connection established successfully without ECN\n");
+ Quit(ECN_SYN_DROP);
+ }
+ break;
+ }
+ }
+
+ /* Test for propogation of CE correctly */
+ DataPkt(session.filename, 3, 0);
+
+ checkECN();
+ return;
+}
+
+void DataPkt (char *filename, u_int8_t iptos, u_int8_t tcp_flags)
+{
+ struct IPPacket *p, *datapkt;
+ struct pcap_pkthdr pi;
+ char *read_packet;
+ int i ;
+ int sendflag = 1 ;
+ u_int16_t lastSeqSent = session.snd_nxt;
+ double startTime = 0;
+ char *dataptr ;
+ char data[MAXREQUESTLEN];
+ int datalen;
+ int ipsz;
+
+ datalen = PrepareRequest (data, filename);
+
+ datapkt = AllocateIPPacket(0, 0, datalen + 1, "ECN (datapkt)");
+
+ dataptr = (char *)datapkt->tcp + sizeof(struct TcpHeader);
+ memcpy((void *)dataptr,(void *)data, datalen);
+
+ ipsz = sizeof(struct IpHeader) + sizeof(struct TcpHeader) + datalen + 1;
+
+ /* send the data packet
+ * we try to "achieve" reliability by
+ * sending the packet upto 5 times, wating for
+ * 2 seconds between packets
+ * BAD busy-wait loop
+ */
+
+ i = 0 ;
+ while(1) {
+
+ if (sendflag == 1) {
+ SendSessionPacket(datapkt, ipsz,
+ TCPFLAGS_PSH | TCPFLAGS_ACK | tcp_flags, 0, 0, iptos);
+
+ startTime = GetTime();
+ sendflag = 0 ;
+ i++ ;
+ }
+
+ /* Check if we have received any packets */
+ if ((read_packet =(char *)CaptureGetPacket(&pi)) != NULL) {
+ p = (struct IPPacket *)FindHeaderBoundaries(read_packet);
+
+ /*
+ * packet that we sent?
+ */
+
+ if (INSESSION(p,session.src, session.sport,
+ session.dst,session.dport) &&
+ (p->tcp->tcp_flags == (TCPFLAGS_PSH | TCPFLAGS_ACK)) &&
+ SEQ_GT(ntohl(p->tcp->tcp_seq), lastSeqSent) &&
+ SEQ_LEQ(ntohl(p->tcp->tcp_ack), session.rcv_nxt)) {
+ lastSeqSent = ntohl(p->tcp->tcp_seq);
+ if (session.debug >= SESSION_DEBUG_LOW) {
+ printf("xmit %d\n", i);
+ PrintTcpPacket(p);
+ }
+ StorePacket(p);
+ session.snd_nxt += datalen + 1;
+ session.totSeenSent ++ ;
+ continue ;
+ }
+
+ /*
+ * from them?
+ */
+ if (INSESSION(p, session.dst, session.dport, session.src,
+ session.sport) && (p->tcp->tcp_flags & TCPFLAGS_ACK) &&
+ (ntohl(p->tcp->tcp_seq) == session.rcv_nxt) &&
+ SEQ_GT(ntohl(p->tcp->tcp_ack), session.snd_una)) {
+ session.snd_una = ntohl(p->tcp->tcp_ack);
+ session.snd_nxt = session.snd_una;
+ if (p->ip->ip_ttl != session.ttl) {
+ session.ttl = p->ip->ip_ttl;
+ }
+ if (session.debug >= SESSION_DEBUG_LOW) {
+ printf("rcvd %d\n", i);
+ PrintTcpPacket(p);
+ }
+ StorePacket(p);
+ session.totRcvd ++;
+ break ;
+ }
+ /*
+ * otherwise, this is a bad packet
+ * we must quit
+ */
+ //processBadPacket(p);
+ }
+ if ((GetTime() - startTime >= 1) && (sendflag == 0) && (i < 3)) {
+ sendflag = 1 ;
+ }
+ if (i >= 3) {
+ printf ("ERROR: sent request 3 times without response\n");
+ return;
+ }
+ }
+
+ FreeIPPacket(&datapkt);
+
+ /* process any response by sending Acks */
+ rcvData (ECNAckData);
+}
+
+void ECNAckData (struct IPPacket *p)
+{
+
+ uint32 seq = history[session.hsz - 1].seqno;
+ uint16 datalen = history[session.hsz - 1].dlen;
+ int fin = history[session.hsz - 1].fin;
+ int rst = history[session.hsz - 1].rst;
+ int i;
+ struct IPPacket *ackpkt = NULL;
+ static int ECT_00 = 0;
+ static int ECT_01 = 0;
+ static int ECT_10 = 0;
+ static int ECT_11 = 0;
+ static int ECN_ECHO = 0;
+ static int send_cwr = 0;
+ static int send_ece = 0;
+ uint8 tcp_flags = 0;
+
+
+ /* Legend:
+ * ECN_ECHO: counts packets with TCP header ECN bit set
+ * ECT_XX: counts packets with ECT codepoint XX (IP)
+ */
+
+ if (datalen > session.mss) {
+ printf ("ERROR: mss=%d datalen=%d\nRETURN CODE: %d\n",
+ session.mss, datalen, MSS_ERR);
+ Quit(MSS_ERR);
+ }
+
+ if (datalen > 0) {
+ char *http_code = (char *)calloc(4, sizeof(char));
+ if (seq - session.irs == 1) {
+ /* Response to request packet --> check HTTP response code */
+ memcpy(http_code, ((char *)(p->tcp) + sizeof(struct TcpHeader) +
+ history[session.hsz - 1].optlen + 9), 3);
+ if (strncmp(http_code, HTTP_OK, 3) != 0) {
+ printf("HTTP ERROR - HTTP RESPONSE CODE: %s\nRETURN CODE: %d\n",
+ http_code, atoi(http_code));
+ Quit(atoi(http_code));
+ }
+ }
+
+ session.totDataPktsRcvd++;
+
+ if (session.verbose) {
+ printf ("r %f %d %d\n",
+ GetTime() - session.epochTime,
+ seq - session.irs,
+ seq - session.irs + datalen);
+ }
+
+ }
+
+ /* Check if packet has the ECN_ECHO flag set */
+ if (history[session.hsz - 1].ecn_echo) {
+ ECN_ECHO += 1;
+ }
+
+ if ((p->ip->ip_tos & 0x17) == 0) {
+ ECT_00 += 1;
+ }
+ if ((p->ip->ip_tos & 0x17) == 1) {
+ ECT_01 += 1;
+ }
+ if ((p->ip->ip_tos & 0x17) == 2) {
+ ECT_10 += 1;
+ }
+ if ((p->ip->ip_tos & 0x17) == 3) {
+ ECT_11 += 1;
+ }
+
+ if(session.maxseqseen < seq + datalen - 1) {
+ session.maxseqseen = seq + datalen - 1;
+ } else {
+ if (datalen > 0) {
+ if (reordered(p) != 1) {
+ session.num_unwanted_drops += 1;
+ }
+ }
+ }
+
+ /* from TCP/IP vol. 2, p 808 */
+ if (SEQ_LEQ(session.rcv_nxt, seq) &&
+ SEQ_LT(seq, (session.rcv_nxt + session.rcv_wnd)) &&
+ SEQ_LEQ(session.rcv_nxt, (seq + datalen)) &&
+ SEQ_LT((seq+datalen-1), (session.rcv_nxt + session.rcv_wnd))) {
+ int start, end;
+ start = seq - session.irs ;
+ end = start + datalen ;
+
+ for (i = start ; i < end ; i++) {
+ session.dataRcvd[i] = 1 ;
+ }
+
+ start = session.rcv_nxt - session.irs ;
+ end = session.mss * session.maxpkts ;
+
+ for (i = start ; i < end ; i++) {
+ if (session.dataRcvd[i] == 0) {
+ break ;
+ }
+ session.rcv_nxt++ ;
+ }
+ }
+
+ if (datalen > 0) {
+ if (session.verbose) {
+ printf ("a %f %d\n", GetTime() - session.epochTime,
+ session.rcv_nxt - session.irs);
+ }
+ ackpkt = AllocateIPPacket(0, 0, 0, "NewECN (ACK)");
+ if ((p->ip->ip_tos & 0x17) == 3) {
+ tcp_flags = TCPFLAGS_ACK | TCPFLAGS_ECN_ECHO;
+ } else {
+ tcp_flags = TCPFLAGS_ACK;
+ }
+
+ if (send_cwr == 2 && send_ece < 2) {
+ /* Send ECE as if a CE was received, we have to get CWR back */
+ send_ece = 1;
+ tcp_flags |= TCPFLAGS_ECN_ECHO;
+ }
+
+ SendSessionPacket (ackpkt,
+ sizeof(struct IpHeader) + sizeof(struct TcpHeader),
+ tcp_flags, 0, 0, 0);
+ }
+
+ if (send_cwr == 0 && (p->tcp->tcp_flags & TCPFLAGS_ECN_ECHO)) {
+ /* Send CWR atleast once if ECN ECHO is set */
+ int datalen;
+ struct IPPacket *datapkt;
+ char *dataptr;
+ char data[MAXREQUESTLEN];
+ int ipsz;
+
+ datalen = PrepareRequest(data, NULL);
+ datapkt = AllocateIPPacket(0, 0, datalen + 1, "ECN (datapkt)");
+ dataptr = (char *)datapkt->tcp + sizeof(struct TcpHeader);
+ memcpy((void *)dataptr, (void *)data, datalen);
+ ipsz = sizeof(struct IpHeader) + sizeof(struct TcpHeader) +
+ datalen + 1;
+
+ SendSessionPacket(datapkt, ipsz,
+ TCPFLAGS_PSH | TCPFLAGS_ACK | TCPFLAGS_CWR, 0, 0, 2);
+
+ session.snd_nxt += (datalen + 1);
+ send_cwr = 1;
+ FreeIPPacket(&datapkt);
+ }
+
+ if (send_cwr == 1 && !(p->tcp->tcp_flags & TCPFLAGS_ECN_ECHO)) {
+ /* ECE was reset in response to CWR, move to the next state of probing */
+ send_cwr = 2;
+ }
+
+ if (send_ece == 1 && (p->tcp->tcp_flags & TCPFLAGS_CWR)) {
+ /* Received CWR in response to ECE */
+ send_ece = 2;
+ }
+
+ if (SEQ_GT(ntohl(p->tcp->tcp_ack), session.snd_una))
+ session.snd_una = ntohl(p->tcp->tcp_ack);
+ if (SEQ_LT(session.snd_nxt, session.snd_una))
+ session.snd_nxt = session.snd_una;
+
+ if (fin || rst) {
+ /* Increment sequence number for FIN rcvd */
+ session.rcv_nxt++;
+ if (ECT_01 == 0 && ECT_10 == 0) {
+ printf("Never received ECT(0) or ECT(1) in ToS field: FAIL\n");
+ }
+ if (ECT_11 > 3) {
+ /* If we received more than 3 CE, flag it as an error */
+ printf("Received too many ECT_CE (%d): FAIL\n", ECT_11);
+ }
+ printf ("Totdata = %d ECN_ECHO: %d ECT00: %d ECT01: %d ECT10: %d ECT11: %d drops: %d\n",
+ session.rcv_nxt - session.irs, ECN_ECHO, ECT_00,
+ ECT_01, ECT_10, ECT_11, session.num_unwanted_drops);
+ if (fin) {
+ SendSessionPacket (ackpkt,
+ sizeof(struct IpHeader) + sizeof(struct TcpHeader),
+ tcp_flags, 0, 0, 0);
+ }
+ checkECN();
+ Quit(SUCCESS);
+ }
+}
+
+void checkECN ()
+{
+ int i;
+ int sr = 0; /* sr=1: SYN/ACK rcvd */
+ int se = 0; /* se=0: no CWR/no ECHO; se=1: no CWR/ECHO; se=2: CWR/ECHO */
+ int ar = 0; /* ar=0: no ACK rcvd; ar=1: ACK rcvd */
+ int ae = 0; /* ae=0: ACK/no ECHO; ae=1: ACK/ECHO */
+ int we = 0; /* we=0: no ECHO; we=1 ECHO/CWR; we=2 ECHO/CWR/ECHO stop */
+ int ee = 0; /* ee=0 never sent ECE; ee=1 sent ECE; ee=2 ECE / CWR */
+
+ for (i = 0 ; i < session.hsz; i++) {
+ if ((history[i].type == RCVD) && (history[i].syn == 1) &&
+ (history[i].ack == 1)) {
+ sr = 1;
+ if (history[i].ecn_echo == 1) {
+ se = 1;
+ if (history[i].cwr == 1) {
+ se = 2;
+ }
+ }
+ }
+ }
+
+ for (i = 0 ; i < session.hsz; i++) {
+ if (history[i].type == RCVD && history[i].syn == 0 &&
+ history[i].ack == 1) {
+ ar = 1;
+ if (history[i].ecn_echo == 1) {
+ ae = 1;
+ }
+ }
+ }
+
+ for (i = 0; i < session.hsz; i++) {
+ if (history[i].type == SENT && history[i].dlen > 0 &&
+ history[i].cwr == 1) {
+ we = 1;
+ continue;
+ }
+ if (we == 1 && history[i].type == RCVD && history[i].ecn_echo == 0) {
+ we = 2;
+ break;
+ }
+ if (we == 2 && history[i].type == RCVD && history[i].ecn_echo == 1) {
+ we = 1;
+ break;
+ }
+ }
+
+ for (i = 0; i < session.hsz; i++) {
+ if (history[i].type == SENT && history[i].ecn_echo == 1) {
+ ee = 1;
+ continue;
+ }
+ if (ee == 1 && history[i].type == RCVD && history[i].dlen > 0 &&
+ history[i].cwr == 1) {
+ /* Received cwr in response to ECE */
+ ee = 2;
+ break;
+ }
+ }
+
+ printf ("sr=%d se=%d ar=%d ae=%d we=%d\n", sr, se, ar, ae, we);
+ switch (sr) {
+ case 0:
+ printf("No SYN/ACK received from server\n");
+ break;
+ case 1:
+ printf("SYN/ACK received: PASS \n");
+ break;
+ default:
+ printf("Unknown value for sr: %d\n", sr);
+ break;
+ }
+ switch (se) {
+ case 0:
+ printf("No CWR or ECE on SYN/ACK, server does not support ECN\n");
+ break;
+ case 1:
+ printf("ECE flag set on SYN/ACK, server supports ECN: PASS\n");
+ break;
+ case 2:
+ printf("Both CWR and ECE set on SYN/ACK, incompatible SYN/ACK\n");
+ break;
+ default:
+ printf("Unknown value for se: %d\n", se);
+ break;
+ }
+
+ switch (ar) {
+ case 0:
+ printf("No ACK received\n");
+ break;
+ case 1:
+ printf("ACK received: PASS\n");
+ break;
+ default:
+ printf("Unknown value for ar: %d\n", ar);
+ break;
+ }
+
+ switch (ae) {
+ case 0:
+ printf("Received ACKS but never ECE\n");
+ break;
+ case 1:
+ printf("Received ACKs with ECE, in response to simulated CE bit: PASS\n");
+ break;
+ default:
+ printf("Unknown value for ae: %d\n", ae);
+ break;
+ }
+
+ switch (we) {
+ case 0:
+ printf("Never received ECE\n");
+ break;
+ case 1:
+ printf("Received ECE and sent CWR\n");
+ break;
+ case 2:
+ printf("Received ECE, sent CWR and stopped receiving ECE afterwards: PASS\n");
+ break;
+ default:
+ printf("Unknown value for we: %d\n", we);
+ break;
+ }
+
+ switch (ee) {
+ case 0:
+ printf("Never sent ECE\n");
+ break;
+ case 1:
+ printf("Sent ECE to simulate receiving CE \n");
+ break;
+ case 2:
+ printf("Sent ECE and received CWR in response: PASS\n");
+ break;
+ default:
+ printf("Unknown value for ee: %d\n", ee);
+ break;
+ }
+ return;
+}
+
+void DataPktPathCheck(char *filename, u_int8_t iptos, u_int8_t tcp_flags)
+{
+ struct IPPacket *p, *datapkt;
+ struct pcap_pkthdr pi;
+ char *read_packet;
+ int i ;
+ int sendflag = 1 ;
+ u_int16_t lastSeqSent = session.snd_nxt;
+ double startTime = 0;
+ char *dataptr;
+ char data[MAXREQUESTLEN];
+ int datalen;
+ int ipsz;
+ unsigned int init_ttl;
+
+ datalen = PrepareRequest (data, filename);
+
+ datapkt = AllocateIPPacket(0, 0, datalen + 1, "ECN (datapkt)");
+
+ dataptr = (char *)datapkt->tcp + sizeof(struct TcpHeader);
+ memcpy((void *)dataptr,(void *)data, datalen);
+
+ ipsz = sizeof(struct IpHeader) + sizeof(struct TcpHeader) + datalen + 1;
+ /* send the data packet
+ * we try to "achieve" reliability by
+ * sending the packet upto 5 times, wating for
+ * 2 seconds between packets
+ * BAD busy-wait loop
+ */
+
+ i = 0 ;
+ init_ttl = 1;
+ while(1) {
+
+ if (sendflag == 1) {
+ session.curr_ttl = ++init_ttl;
+ if (init_ttl > 64) /* reached the max */
+ break;
+ SendSessionPacket(datapkt, ipsz,
+ TCPFLAGS_PSH | TCPFLAGS_ACK | tcp_flags, 0, 0, 0x3);
+
+ startTime = GetTime();
+ sendflag = 0;
+ i++ ;
+ }
+
+ /* Check if we have received any packets */
+ if ((read_packet =(char *)CaptureGetPacket(&pi)) != NULL) {
+
+ p = (struct IPPacket *)FindHeaderBoundaries(read_packet);
+
+ /*
+ * packet that we sent?
+ */
+
+ if (INSESSION(p,session.src, session.sport,
+ session.dst,session.dport) &&
+ (p->tcp->tcp_flags == (TCPFLAGS_PSH | TCPFLAGS_ACK)) &&
+ SEQ_GT(ntohl(p->tcp->tcp_seq), lastSeqSent) &&
+ SEQ_LEQ(ntohl(p->tcp->tcp_ack), session.rcv_nxt)) {
+ lastSeqSent = ntohl(p->tcp->tcp_seq);
+ if (session.debug >= SESSION_DEBUG_LOW) {
+ printf("xmit %d\n", i);
+ PrintTcpPacket(p);
+ }
+ StorePacket(p);
+ session.snd_nxt += datalen + 1;
+ session.totSeenSent ++ ;
+ continue ;
+ }
+
+ /*
+ * from them?
+ */
+ if (INSESSION(p, session.dst, session.dport, session.src,
+ session.sport) && (p->tcp->tcp_flags & TCPFLAGS_ACK) &&
+ (ntohl(p->tcp->tcp_seq) == session.rcv_nxt) &&
+ ntohl(p->tcp->tcp_ack) == session.snd_nxt) {
+ session.snd_una = ntohl(p->tcp->tcp_ack);
+ session.snd_nxt = session.snd_una;
+ if (p->ip->ip_ttl != session.ttl) {
+ session.ttl = p->ip->ip_ttl;
+ }
+ if (session.debug >= SESSION_DEBUG_LOW) {
+ printf("rcvd %d\n", i);
+ PrintTcpPacket(p);
+ }
+ StorePacket(p);
+ session.totRcvd ++;
+ break ;
+ }
+ /*
+ * ICMP ttl exceeded
+ */
+ if (p->ip->ip_p == IPPROTOCOL_ICMP) {
+ uint16_t ip_hl;
+ struct IcmpHeader *icmp;
+ ip_hl = (p->ip->ip_vhl & 0x0f) << 2;
+ void *nexthdr;
+
+ icmp = (struct IcmpHeader *)((char *)p->ip + ip_hl);
+ nexthdr = (void *)icmp;
+ if (icmp->icmp_type == ICMP_TIMXCEED) {
+ struct IpHeader off_ip;
+ struct TcpHeader off_tcp;
+
+ bzero(&off_ip, sizeof(struct IpHeader));
+ nexthdr = nexthdr + sizeof(struct IcmpHeader);
+ memcpy((void *)&off_ip, nexthdr,
+ sizeof(struct IpHeader));
+ nexthdr += sizeof(struct IpHeader);
+
+ bzero(&off_tcp, sizeof(struct TcpHeader));
+ memcpy(&off_tcp, nexthdr, 4);
+ if (session.src == off_ip.ip_src &&
+ session.dst == off_ip.ip_dst) {
+ printf("Received ICMP TTL exceeded from %s:"
+ "ttl used %u ip_tos 0x%x sport %u dport %u\n",
+ InetAddress(p->ip->ip_src), init_ttl, off_ip.ip_tos,
+ ntohs(off_tcp.tcp_sport), ntohs(off_tcp.tcp_dport));
+ }
+ }
+ }
+ /*
+ * otherwise, this is a bad packet
+ * we must quit
+ */
+ //processBadPacket(p);
+ }
+ if ((GetTime() - startTime >= 1) && (sendflag == 0)) {
+ sendflag = 1;
+ session.snd_nxt = session.snd_una;
+ }
+ }
+
+ FreeIPPacket(&datapkt);
+}
+void ECNPathCheckTest(u_int32_t sourceAddress, u_int16_t sourcePort,
+ u_int32_t targetAddress, u_int16_t targetPort, int mss)
+{
+ int rawSocket, rc, flag;
+
+ arc4random_stir();
+
+ session.src = sourceAddress;
+ session.sport = sourcePort;
+ session.dst = targetAddress;
+ session.dport = targetPort;
+ session.rcv_wnd = 5*mss;
+ session.snd_nxt = arc4random();
+ session.iss = session.snd_nxt;
+ session.rcv_nxt = 0;
+ session.irs = 0;
+ session.mss = mss;
+ session.maxseqseen = 0;
+ session.epochTime = GetTime();
+ session.maxpkts = 1000;
+ session.debug = 0;
+
+ if ((session.dataRcvd = (u_int8_t *)calloc(sizeof(u_int8_t),
+ mss * session.maxpkts)) == NULL) {
+ printf("no memory to store data, error: %d \n", ERR_MEM_ALLOC);
+ Quit(ERR_MEM_ALLOC);
+ }
+
+ if ((rawSocket = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) {
+ perror("ERROR: couldn't open socket:");
+ Quit(ERR_SOCKET_OPEN);
+ }
+
+ flag = 1;
+ if (setsockopt(rawSocket, IPPROTO_IP, IP_HDRINCL,
+ (char *)&flag, sizeof(flag)) < 0) {
+ perror("ERROR: couldn't set raw socket options:");
+ Quit(ERR_SOCKOPT);
+ }
+
+ session.socket = rawSocket;
+
+ /* Establish a TCP connections with ECN bits */
+ rc = EstablishTcpConnection(TCPFLAGS_ECN_ECHO | TCPFLAGS_CWR, 0);
+ switch (rc) {
+ case ESTABLISH_FAILURE_EARLY_RST:
+ {
+ /* Received a RST when ECN bits are used. Try again without ECN */
+ rc = EstablishTcpConnection(0, 0);
+ if (rc == ESTABLISH_FAILURE_EARLY_RST) {
+ printf("Received RST with or without ECN negotiation\n");
+ printf("The server might not be listening on this port\n");
+ Quit(EARLY_RST);
+ } else if (rc == ESTABLISH_SUCCESS) {
+ printf("Received RST with ECN.\n");
+ printf("Connection established successfully without ECN\n");
+ Quit(ECN_SYN_DROP);
+ } else if (rc == ESTABLISH_FAILURE_NO_REPLY) {
+ printf("Received RST with ECN\n");
+ printf("Exceed max SYN retransmits without ECN\n");
+ Quit(NO_CONNECTION);
+ }
+ break;
+ }
+ case ESTABLISH_FAILURE_NO_REPLY:
+ {
+ /* No reply after retring, try again without ECN */
+ rc = EstablishTcpConnection(0, 0);
+ if (rc == ESTABLISH_FAILURE_EARLY_RST) {
+ printf("Exceeded max SYN retransmits with ECN\n");
+ printf("Received RST without ECN\n");
+ Quit(NO_CONNECTION);
+ } else if (rc == ESTABLISH_FAILURE_NO_REPLY) {
+ printf("Exceeded max SYN retransmits with ECN\n");
+ printf("Exceeded max SYN retransmits without ECN\n");
+ Quit(NO_CONNECTION);
+ } else {
+ printf("Exceeded max SYN retransmits with ECN\n");
+ printf("Connection established successfully without ECN\n");
+ Quit(ECN_SYN_DROP);
+ }
+ break;
+ }
+ }
+
+ DataPktPathCheck(session.filename, 3, 0);
+ return;
+}
+
+
+void
+SynTest(u_int32_t sourceAddress, u_int16_t sourcePort,
+ u_int32_t targetAddress, u_int16_t targetPort, int mss, int syn_reply)
+{
+ int rawSocket, flag;
+ struct IPPacket *synPacket = NULL, *ackPacket = NULL;
+ char *read_packet;
+ struct pcap_pkthdr pi;
+ int synAckReceived = 0;
+ int numRetransmits = 0;
+ double timeoutTime;
+ int tcpoptlen = 4; /* For negotiating MSS */
+ u_int8_t *opt = NULL;
+ struct IPPacket *p = NULL;
+
+ arc4random_stir();
+
+ session.src = sourceAddress;
+ session.sport = sourcePort;
+ session.dst = targetAddress;
+ session.dport = targetPort;
+ session.rcv_wnd = 5*mss;
+ session.snd_nxt = arc4random();
+ session.iss = session.snd_nxt;
+ session.rcv_nxt = 0;
+ session.irs = 0;
+ session.mss = mss;
+ session.maxseqseen = 0;
+ session.epochTime = GetTime();
+ session.maxpkts = 1000;
+
+ if ((session.dataRcvd = (u_int8_t *)calloc(sizeof(u_int8_t),
+ mss * session.maxpkts)) == NULL) {
+ printf("no memory to store data, error: %d \n", ERR_MEM_ALLOC);
+ Quit(ERR_MEM_ALLOC);
+ }
+
+ if ((rawSocket = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) {
+ perror("ERROR: couldn't open socket:");
+ Quit(ERR_SOCKET_OPEN);
+ }
+
+ flag = 1;
+ if (setsockopt(rawSocket, IPPROTO_IP, IP_HDRINCL,
+ (char *)&flag, sizeof(flag)) < 0) {
+ perror("ERROR: couldn't set raw socket options:");
+ Quit(ERR_SOCKOPT);
+ }
+
+ session.socket = rawSocket;
+
+
+ /* allocate the syn packet -- Changed for new IPPacket structure */
+ synPacket = AllocateIPPacket(0, tcpoptlen, 0, "ECN (SYN)");
+ opt = (((u_int8_t *)synPacket->tcp) + sizeof(struct TcpHeader));
+ opt[0] = (u_int8_t)TCPOPT_MAXSEG;
+ opt[1] = (u_int8_t)TCPOLEN_MAXSEG;
+ *((u_int16_t *)((u_int8_t *)opt + 2)) = htons(session.mss);
+
+ SendSessionPacket(synPacket,
+ sizeof(struct IpHeader) + sizeof(struct TcpHeader) + tcpoptlen,
+ TCPFLAGS_SYN , 0, tcpoptlen, 0);
+ timeoutTime = GetTime() + 1;
+
+ /*
+ * Wait for SYN/ACK and retransmit SYN if appropriate
+ * not great, but it gets the job done
+ */
+
+ while(!synAckReceived && numRetransmits < 3) {
+ while(GetTime() < timeoutTime) {
+ /* Have we captured any packets? */
+ if ((read_packet = (char *)CaptureGetPacket(&pi)) != NULL) {
+ p = (struct IPPacket *)FindHeaderBoundaries(read_packet);
+ /* Received a packet from us to them */
+ if (INSESSION(p, session.src, session.sport,
+ session.dst, session.dport)) {
+ /* Is it a SYN/ACK? */
+ if (p->tcp->tcp_flags & TCPFLAGS_SYN) {
+ if (session.debug >= SESSION_DEBUG_LOW) {
+ PrintTcpPacket(p);
+ }
+ StorePacket(p);
+ session.totSeenSent++ ;
+ } else {
+ processBadPacket(p);
+ }
+ continue;
+ }
+
+ /* Received a packet from them to us */
+ if (INSESSION(p, session.dst, session.dport, session.src,
+ session.sport)) {
+ /* Is it a SYN/ACK? */
+ if ((p->tcp->tcp_flags & TCPFLAGS_SYN) &&
+ (p->tcp->tcp_flags & TCPFLAGS_ACK)) {
+ timeoutTime = GetTime(); /* force exit */
+ synAckReceived++;
+ if (session.debug >= SESSION_DEBUG_LOW) {
+ PrintTcpPacket(p);
+ }
+ StorePacket(p);
+
+ /*
+ * Save ttl for,admittedly poor,indications of reverse
+ * route change
+ */
+ session.ttl = p->ip->ip_ttl;
+ session.snd_wnd = ntohl(p->tcp->tcp_win);
+ session.totRcvd ++;
+ break;
+ } else {
+ if ((p->tcp->tcp_flags)& (TCPFLAGS_RST)) {
+ printf ("ERROR: EARLY_RST\n");
+ goto done;
+ }
+ }
+ }
+ }
+ }
+
+ if (!synAckReceived) {
+ if (session.debug >= SESSION_DEBUG_LOW) {
+ printf("SYN timeout. Retransmitting\n");
+ }
+ SendSessionPacket(synPacket,
+ sizeof(struct IpHeader) + sizeof(struct TcpHeader) + tcpoptlen,
+ TCPFLAGS_SYN , 0, tcpoptlen, 0);
+ timeoutTime = GetTime() + 1;
+ numRetransmits++;
+ }
+ }
+
+ if (numRetransmits >= 3) {
+ printf("ERROR: No connection after 3 retries...\nRETURN CODE: %d\n",
+ NO_CONNECTION);
+ goto done;
+ }
+ if (session.debug >= SESSION_DEBUG_LOW)
+ printf("Received SYN-ACK\n");
+ if (syn_reply != 0) {
+ /* Update session variables */
+ session.irs = ntohl(p->tcp->tcp_seq);
+ session.dataRcvd[0] = 1 ;
+ session.rcv_nxt = session.irs + 1; /* SYN/ACK takes up a byte of seq space */
+ session.snd_nxt = session.iss + 1; /* SYN takes up a byte of seq space */
+ session.snd_una = session.iss + 1;
+ session.maxseqseen = ntohl(p->tcp->tcp_seq);
+ session.initSession = 1;
+ if (session.debug >= SESSION_DEBUG_LOW) {
+ printf("try to send the %s\n", syn_reply == TCPFLAGS_ACK ? "third Ack" : "RST");
+ printf("src = %s:%d (%u)\n", InetAddress(session.src),
+ session.sport, session.iss);
+ printf("dst = %s:%d (%u)\n",InetAddress(session.dst),
+ session.dport, session.irs);
+ }
+
+ /* allocate the syn packet -- Changed for new IPPacket structure */
+ ackPacket = AllocateIPPacket(0, 0, 0, "SYN reply");
+ /* send an ACK */
+ SendSessionPacket(ackPacket,
+ sizeof(struct IpHeader) + sizeof(struct TcpHeader),
+ syn_reply, 0, 0, 0);
+ FreeIPPacket(&ackPacket);
+ }
+done:
+ FreeIPPacket(&synPacket);
+}