#ifndef TESTTCP_H #define TESTTCP_H #include <endian.h> #pragma pack (1) //ip protocol header struct IPHeader { unsigned char headerLen:4; unsigned char version:4; unsigned char tos; //service type unsigned short totalLen; //total length unsigned short id; //identity unsigned short flagOffset; //3-bit flag + 13-bit slice offset unsigned char ttl; //TTL unsigned char protocol; //protocol unsigned short checksum; //header checksum unsigned int srcIP; //source IP address unsigned int dstIP; //destination IP address }; //TCP header struct TCPHeader { unsigned short srcPort; //source port unsigned short dstPort; //destination port //Packet sequence number If the TCP data is too large (greater than the allowable level of IP packets), it must be segmented. This Sequence Number is to record the sequence number of each packet, //The receiver can reassemble the TCP data. The value of the sequence number field refers to the sequence number of the first byte of the data sent in this segment unsigned int seqNum; //Acknowledgement number In order to confirm that the host side has indeed received the packet data sent by our client side, our client side certainly hopes to receive a response from the host side, that is the purpose of this Acknowledge Number //When the client side receives this confirmation code, it can be sure that the previously transmitted packet has been received correctly. This number is the sequence number of the first byte of the data expected to receive the next segment of the other party unsigned int ackNum; #if LITTLE_ENDIAN unsigned char reserved1:4; //Reserve 4-bit header length of 6 bits unsigned char headerLen:4; //tcp header length //flags unsigned char fin:1; // Used to release a connection. When FIN=1, it means that the connection is required to be released. unsigned char syn:1; //Sync When SYN=1, it means this is a connection request or connection acceptance message. unsigned char rst:1; //Reset, when a serious error occurs in the TCP connection, the connection must be released. unsigned char psh:1; //When two applications communicate, when PSH=1, it means "push" to the application as soon as possible, instead of waiting for the buffer to be full before delivering upwards. unsigned char ack:1; //When ACK=1, the acknowledgment field is valid, and all segments after the connection is established must set ACK to 1. unsigned char urg:1; //Emergency pointer, when URG=1, it means that the urgent pointer is valid and should be sent as soon as possible. Used to handle to avoid interruption of TCP data flow unsigned char reseverd2:2; //Reserve 2 of 6 bits #else unsigned char headerLen:4; //tcp header length unsigned char reserved1:4; //Reserve 4-bit header length of 6 bits unsigned char reseverd2:2; //Reserve 2 of 6 bits unsigned char urg:1; //When URG=1, it means that the urgent pointer is valid and should be sent as soon as possible. unsigned char ack:1; //When ACK=1, the acknowledgment field is valid, and all segments after the connection is established must set ACK to 1. unsigned char psh:1; //When two applications communicate, when PSH=1, it means "push" to the application as soon as possible, instead of waiting for the buffer to be full before delivering upwards. unsigned char rst:1; //Reset, when a serious error occurs in the TCP connection, the connection must be released. unsigned char syn:1; //Sync When SYN=1, it means this is a connection request or connection acceptance message. unsigned char fin:1; // Used to release a connection. When FIN=1, it means that the connection is required to be released. #endif unsigned short windownSize; //16-bit window size unsigned short checksum; //16-bit TCP checksum unsigned short urgPtr; //16-bit urgent pointer when URG=1. It means the urgent pointer is valid. It should be sent as soon as possible, not in the original queue order }; //TCP pseudo header struct PseudoHeader { unsigned int srcIP; //source address unsigned int dstIP; //destination address unsigned char mustBeZero;//empty, used to fill alignment unsigned char protocol; //protocol type unsigned short len; //TCP length }; //Max Segment Size maximum segment length struct MSSOption { unsigned char kind; unsigned char length; unsigned short maxValue; }; //Selective Acknowledgment struct SACKOption { unsigned char kind; unsigned char length; }; //rtt is equal to the current time tcp_time_stamp minus Timestamp Echo Reply struct TimestampsOption { unsigned char kind; unsigned char length; unsigned int timestamp; unsigned int timestampReply; }; struct NoOperation { unsigned char type; }; struct WindowScaleOption { unsigned char kind; unsigned char length; unsigned char shiftCount; }; #pragma pack () #endif // TESTTCP_H
testTcp.cpp
#include "testTcp.h" #include <iostream> #include <stdio.h> #include <string.h> #include <arpa/inet.h> #include <netinet/ip.h> #include <errno.h> using namespace std; int g_tcpSeqNum = 111; int g_tcpAckNum = 0; //ip number to string void ipLLToStr(long long ip_num,char* ip_str) { unsigned int iptok1 = (ip_num & 0xFF000000) >> 24; unsigned int iptok2 = (ip_num & 0x00FF0000) >> 16; unsigned int iptok3 = (ip_num & 0x0000FF00) >> 8; unsigned int iptok4 = ip_num & 0x000000FF; char ip[32]; bzero(ip,sizeof(ip)); snprintf(ip,sizeof(ip),"%d.%d.%d.%d",iptok1,iptok2,iptok3,iptok4); strcpy(ip_str,ip); } //print ip header void printIPHeader(IPHeader* ipHeader) { char srcIPStr[64] = "",dstIPStr[64]=""; ipLLToStr(ntohl(ipHeader->srcIP),srcIPStr); ipLLToStr(ntohl(ipHeader->dstIP),dstIPStr); int totalLen = ntohs(ipHeader->totalLen); char ipHeaderStr[256] = ""; snprintf(ipHeaderStr,sizeof(ipHeaderStr),"ip header: version:%d,tos:%d,protocol:%d,ttl:%d,srcIP:%s,dstIP:%s,totalLen:%d" ,ipHeader->version,ipHeader->tos,ipHeader->protocol,ipHeader->ttl,srcIPStr,dstIPStr,totalLen); cout << ipHeaderStr << endl; } //print tcp header void printTCPHeader(TCPHeader* tcpHeader) { char tcpHeaderStr[256] = ""; snprintf(tcpHeaderStr,sizeof(tcpHeaderStr),"tcp header:srcPort:%d,dstPort:%d,seqNum:%u,ackNum:%u,headerLen:%d,fin:%d,syn:%d,rst:%d" ",psh:%d,ack:%d,urg:%d,windowsSize:%d,checksum:%d,urgPtr:%d",ntohs(tcpHeader->srcPort),ntohs(tcpHeader->dstPort) ,ntohl(tcpHeader->seqNum),ntohl(tcpHeader->ackNum),tcpHeader->headerLen<<2,tcpHeader->fin,tcpHeader->syn,tcpHeader->rst ,tcpHeader->psh,tcpHeader->ack,tcpHeader->urg,ntohs(tcpHeader->windownSize),ntohs(tcpHeader->checksum),tcpHeader->urgPtr); cout << tcpHeaderStr << endl; } //Construct IP header void buildIPHeader(IPHeader* ipHeader,int totalLen,int srcIPNum,int dstIPNum) { ipHeader->headerLen = sizeof(IPHeader)>>2; ipHeader->version = IPVERSION; //Service type ipHeader->tos = 0; ipHeader->totalLen = htons(totalLen); ipHeader->id=htons(0); //Set the flag to 0 ipHeader->flagOffset=htons(0x02 << 13); ; //The protocol used is the TCP protocol ipHeader->protocol=IPPROTO_TCP; // How long a packet can live on the network ipHeader->ttl=64; ipHeader->srcIP = srcIPNum; ipHeader->dstIP = dstIPNum; } //The checksum algorithm of tcp and udp is the same unsigned short calChecksum(unsigned short* buffer, int size) { unsigned long cksum = 0; while(size>1) { cksum += *buffer++; size -= sizeof(unsigned short); } if(size){ cksum += *(unsigned char*)buffer; } cksum = (cksum>>16) + (cksum&0xffff); //Add high 16bit and low 16bit cksum += (cksum>>16); //Add the 16bit of the carry to the high order and the low 16bit return (unsigned short)(~cksum); } //Construct tcp protocol header void buildTCPHeader(TCPHeader* tcpHeader,sockaddr_in* srcAddr,sockaddr_in* dstAddr,IPHeader* ipHeader,int headerLen,int seqNum,int ackNum,char syn,char ack) { tcpHeader->srcPort = srcAddr->sin_port; tcpHeader->dstPort = dstAddr->sin_port; tcpHeader->seqNum = htonl(seqNum); tcpHeader->ackNum = htonl(ackNum); tcpHeader->urg = 0; tcpHeader->ack = ack; tcpHeader->psh = 0; tcpHeader->rst = 0; tcpHeader->syn = syn; tcpHeader->end = 0; tcpHeader->windownSize = htons(14600); tcpHeader->checksum = 0; tcpHeader->urgPtr = 0; tcpHeader->headerLen = headerLen>>2; / / Calculate the checksum of the ip header according to the buf of the pseudo header char psdHeaderBuf[256] = ""; PseudoHeader* psdHeader = (PseudoHeader*)psdHeaderBuf; psdHeader->srcIP = ipHeader->srcIP; psdHeader->dstIP = ipHeader->dstIP; psdHeader->mustBeZero = 0; psdHeader->protocol = ipHeader->protocol; psdHeader->len = htons(sizeof(TCPHeader)); memcpy(psdHeaderBuf+sizeof(PseudoHeader),tcpHeader,sizeof(TCPHeader)); tcpHeader->checksum = calChecksum((unsigned short*)psdHeaderBuf, sizeof(PseudoHeader)+sizeof(TCPHeader)); } //send syn int sendTcp(int sockfd,sockaddr_in* srcAddr,sockaddr_in* dstAddr,char syn,char ack,bool addMssOption,bool addSACKOption,bool addTsOption,bool addNoOption ,bool addWindowScale) { char buf[1024] = ""; int totalLen = sizeof(IPHeader) + sizeof(TCPHeader); if(addMssOption){ totalLen += sizeof(MSSOption); } if(addSACKOption){ totalLen += sizeof(SACKOption); } if(addTsOption){ totalLen += sizeof(TimestampsOption); } if(addNoOption){ totalLen + = sizeof (NoOperation); } if(addWindowScale){ totalLen += sizeof(WindowScaleOption); } int pos = 0; IPHeader* ipHeader = (IPHeader*)buf; buildIPHeader(ipHeader,totalLen,srcAddr->sin_addr.s_addr,dstAddr->sin_addr.s_addr); printIPHeader(ipHeader); pos += sizeof(IPHeader); TCPHeader* tcpHeader = (TCPHeader*)(buf+pos); buildTCPHeader(tcpHeader,srcAddr,dstAddr,ipHeader,totalLen-sizeof(IPHeader),g_tcpSeqNum++,g_tcpAckNum,syn,ack); printTCPHeader (tcpHeader); pos += sizeof(TCPHeader); if(addMssOption) { MSSOption* mssOption = (MSSOption*)(buf+pos); mssOption->kind = 2;//htons(2); mssOption->length = sizeof(MSSOption);//htons(4); mssOption->maxValue = htons(1460); pos += sizeof(MSSOption); } if(addSACKOption) { SACKOption* sackOption = (SACKOption*)(buf+pos); sackOption->kind = 4; sackOption->length = sizeof(SACKOption); pos += sizeof(SACKOption); } if(addTsOption) { TimestampsOption* tsOption = (TimestampsOption*)(buf+pos); tsOption->kind = 8; tsOption->length = sizeof(TimestampsOption); tsOption->timestamp = htonl(111); tsOption->timestampReply = 0; pos += sizeof(TimestampsOption); } if(addNoOption) { NoOperation * noOption = (NoOperation *) (buf + pos); noOption->type = 1; pos + = sizeof (NoOperation); } if(addWindowScale) { WindowScaleOption* windowScaleOption = (WindowScaleOption*)(buf+pos); windowScaleOption->kind = 3; windowScaleOption->length = sizeof(WindowScaleOption); windowScaleOption->shiftCount = 6; } if(sendto(sockfd,buf,totalLen,0,(struct sockaddr *)dstAddr,sizeof(*dstAddr))<0){ perror("sendto error"); } return 0; } // Analysis syn + ack void parseSynAck(int sockfd,sockaddr_in* dstAddr) { char recvBuf[1024] = ""; socklen_t cliLen = sizeof(dstAddr); int recvLen = recvfrom(sockfd,recvBuf,sizeof(recvBuf),0,(sockaddr*)&dstAddr,&cliLen); if(recvLen<=0) { cout << "parse syc ack fail,recvLen:" << recvLen << endl; return; } int pos = 0; IPHeader* ipHeader = (IPHeader*)recvBuf; pos += sizeof(IPHeader); printIPHeader(ipHeader); TCPHeader* tcpHeader = (TCPHeader*)(recvBuf+pos); printTCPHeader (tcpHeader); g_tcpSeqNum = ntohl(tcpHeader->ackNum); } intmain() { int sockfd = socket (AF_INET, SOCK_RAW, IPPROTO_TCP); if( sockfd < 0) { cout << strerror(errno) << endl; return -1; } char srcStr[32] = "172.16.96.52"; char dstStr[32] = "192.168.145.51"; int srcPort = 50852; int dstPort = 6699; sockaddr_in srcAddr; bzero(&srcAddr,sizeof(srcAddr)); srcAddr.sin_family = AF_INET; srcAddr.sin_addr.s_addr = inet_addr(srcStr); srcAddr.sin_port = htons(srcPort); sockaddr_in dstAddr; bzero(&dstAddr,sizeof(dstAddr)); dstAddr.sin_family = AF_INET; dstAddr.sin_addr.s_addr = inet_addr(dstStr); dstAddr.sin_port = htons(dstPort); //IPPROTO_TP indicates that the user fills in the IP packet by himself //IP_HDRINCL means that the kernel calculates the header checksum of the IP packet and fills the id of that IP int on = 1; setsockopt(sockfd,IPPROTO_IP,IP_HDRINCL,&on,sizeof(on)); cout << "syn:" << endl; char syn = 1, ack = 0; sendTcp(sockfd,&srcAddr,&dstAddr,syn,ack,true,true,true,true,true); cout << "syn+ack:" << endl; parseSynAck(sockfd,&dstAddr); //ack // syn = 0, ack = 1; // sendTcp(sockfd,&srcAddr,&dstAddr,syn,ack,true,true,true,true,true); return 0; } syn: ip header: version:4,tos:0,protocol:6,ttl:64,srcIP:172.16.96.52,dstIP:192.168.145.51,totalLen:60 tcp header:srcPort:50852,dstPort:6699,seqNum:111,ackNum:0,headerLen:40,fin:0,syn:1,rst:0,psh:0,ack:0,urg:0,windowsSize:14600,checksum:59258,urgPtr:0 syn + ack: ip header: version:4,tos:0,protocol:6,ttl:62,srcIP:192.168.145.51,dstIP:172.16.96.52,totalLen:60 tcp header:srcPort:6699,dstPort:50852,seqNum:3363298119,ackNum:112,headerLen:40,fin:0,syn:1,rst:0,psh:0,ack:1,urg:0,windowsSize:14480,checksum:34955,urgPtr:0 //Theoretically we should send an ack here, but in fact, after sending syn, the operating system kernel receives syc+ack, it checks the socket in the kernel, //It is found that no socket corresponds to this packet, so it automatically replies to rst and closes the connection, so we can no longer reply to ack because the connection has been disconnected