rawsocket发送tcp包

本文详细解析了TCP协议的三次握手过程,包括发送SYN、SYN-ACK及ACK的具体实现方式,并展示了如何通过代码构建IP及TCP头部信息。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

testTcp.h

#ifndef TESTTCP_H
#define TESTTCP_H
#include <endian.h>

#pragma pack(1)

//ip协议头
struct IPHeader
{
unsigned char headerLen:4;
unsigned char version:4;
unsigned char tos; //服务类型
unsigned short totalLen; //总长度
unsigned short id; //标识
unsigned short flagOffset; //3位标志+13位片偏移
unsigned char ttl; //TTL
unsigned char protocol; //协议
unsigned short checksum; //首部检验和
unsigned int srcIP; //源IP地址
unsigned int dstIP; //目的IP地址
};

//TCP首部
struct TCPHeader
{
unsigned short srcPort; //源端口
unsigned short dstPort; //目的端口
//封包序号 如果 TCP 数据太大时(大于 IP 封包的容许程度), 就得要进行分段.这个 Sequence Number 就是记录每个封包的序号,
//可以让收受端重新将 TCP 的数据组合起来。序号字段的值则指的是本报文段所发送的数据的第一个字节的序号
unsigned int seqNum;
//确认号 为了确认主机端确实有收到我们 client 端所送出的封包数据,我们 client 端当然希望能够收到主机方面的响应,那就是这个 Acknowledge Number 的用途了
//当 client 端收到这个确认码时,就能够确定之前传递的封包已经被正确的收下了.这个号是期望收到对方的下一个报文段的数据的第一个字节的序号
unsigned int ackNum;
#if LITTLE_ENDIAN
unsigned char reserved1:4; //保留6位中的4位首部长度
unsigned char headerLen:4; //tcp头部长度
//flags
unsigned char fin:1; // 用来释放一个连接。当FIN=1时表示要求释放连接。
unsigned char syn:1; //同步当SYN=1时,表示这是一个连接请求或连接接受报文。
unsigned char rst:1; //复位,当TCP连接中出现了严重差错,必须释放连接。
unsigned char psh:1; //当两个应用程序进行通信时,当PSH=1时,表示尽快地用“推送”给应用程序,而不用等到缓冲区满了再向上交付。
unsigned char ack:1; //当ACK=1时,确认字段有效,在连接建立后的所有报文段都必须把ACK置为1。
unsigned char urg:1; //紧急指针,当URG=1时,表示紧急指针有效,应该尽快传送。用来处理避免TCP数据流中断
unsigned char reseverd2:2; //保留6位中的2位
#else
unsigned char headerLen:4; //tcp头部长度
unsigned char reserved1:4; //保留6位中的4位首部长度
unsigned char reseverd2:2; //保留6位中的2位
unsigned char urg:1; //当URG=1时,表示紧急指针有效,应该尽快传送。
unsigned char ack:1; //当ACK=1时,确认字段有效,在连接建立后的所有报文段都必须把ACK置为1。
unsigned char psh:1; //当两个应用程序进行通信时,当PSH=1时,表示尽快地用“推送”给应用程序,而不用等到缓冲区满了再向上交付。
unsigned char rst:1; //复位,当TCP连接中出现了严重差错,必须释放连接。
unsigned char syn:1; //同步当SYN=1时,表示这是一个连接请求或连接接受报文。
unsigned char fin:1; // 用来释放一个连接。当FIN=1时表示要求释放连接。
#endif
unsigned short windownSize; //16位窗口大小
unsigned short checksum; //16位TCP检验和
unsigned short urgPtr; //16位紧急指针 当URG=1时.表示紧急指针有效.应该尽快传送,不要按本来的列队次序传送
};

//TCP伪首部
struct PseudoHeader
{
unsigned int srcIP; //源地址
unsigned int dstIP; //目的地址
unsigned char mustBeZero;//置空,用于填充对齐
unsigned char protocol; //协议类型
unsigned short len; //TCP长度
};

//Max Segment Size最大segment长度
struct MSSOption
{
unsigned char kind;
unsigned char length;
unsigned short maxValue;
};

//Selective Acknowledgment
struct SACKOption
{
unsigned char kind;
unsigned char length;
};

//rtt即等于现在的时间tcp_time_stamp减去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数字转字符串
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);
}

//打印ip头
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;
}

//打印tcp头
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;
}

//构造IP头
void buildIPHeader(IPHeader* ipHeader,int totalLen,int srcIPNum,int dstIPNum)
{
ipHeader->headerLen = sizeof(IPHeader)>>2;
ipHeader->version = IPVERSION;
//服务类型
ipHeader->tos = 0;
ipHeader->totalLen = htons(totalLen);
ipHeader->id=htons(0);
//设置flag标记为0
ipHeader->flagOffset=htons(0x02 << 13); ;
//运用的协议为TCP协议
ipHeader->protocol=IPPROTO_TCP;
//一个封包在网络上可以存活的时间
ipHeader->ttl=64;
ipHeader->srcIP = srcIPNum;
ipHeader->dstIP = dstIPNum;
}

//tcp和udp的校验和算法是相同的
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); //将高16bit与低16bit相加
cksum += (cksum>>16); //将进位到高位的16bit与低16bit 再相加
return (unsigned short)(~cksum);
}

//构造tcp协议头
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->fin = 0;
tcpHeader->windownSize = htons(14600);
tcpHeader->checksum = 0;
tcpHeader->urgPtr = 0;
tcpHeader->headerLen = headerLen>>2;

//根据伪首部的buf计算ip头的校验和
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));
}

//发送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;
}

//解析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);
}

int main()
{
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说明用户自己填写IP报文
//IP_HDRINCL表示由内核来计算IP报文的头部校验和,和填充那个IP的id
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

//理论上在这我们应该发送一个ack,但实际上在发完syn后,操作系统内核收到syc+ack,它检查内核里的socket,
//发现没有一个socket对应于这个包,于是自动回复rst,关闭连接,所以我们无法再回复ack,因为连接已断开
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值