rawsocket send tcp packet

testTcp.h
#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

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=326766723&siteId=291194637
Recommended