先附代碼
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define IP_DF 0x4000 /* Flag: "Don't Fragment" */
//tcp 協(xié)議首部
typedef struct tcphdr {
unsigned short sport;//源端口號
unsigned short dport;//目的端口號
unsigned int seq;//32位序列號
unsigned int ack_seq;//32位確認(rèn)號
unsigned char len; // 首部長度
unsigned char flag; // 標(biāo)志位
unsigned short window; //16位窗口大小
unsigned short check; //16位校驗(yàn)和
unsigned short urg_ptr; //16位緊急指針
}TCPHDR;
//偽首部
typedef struct pseudohdr {
unsigned int saddr; //源ip
unsigned int daddr; //目的ip
char zeros; // 8為保留字節(jié),為0
char protocol; //傳輸層協(xié)議號,tcp為6
unsigned short length; //16位tcp報文長度(tcp首部 + 數(shù)據(jù))
}PSEUDOHDR;
// ip 協(xié)議首部
typedef struct iphdr {
unsigned char ver_and_hdrLen; // 版本號和ip頭部長度
unsigned char tos; // 服務(wù)類型
unsigned short tot_len; // 總長度(首部和數(shù)據(jù)之和的長度)
unsigned short id; // IP包ID
unsigned short frag_off; // 標(biāo)志位(包括分片偏移量)
unsigned char ttl; // 生命周期
unsigned char protocol; // 上層協(xié)議
unsigned short check; // 校驗(yàn)和
unsigned int saddr; // 源IP地址
unsigned int daddr; // 目標(biāo)IP地址
}IPHDR;
//生成校驗(yàn)和,tcp校驗(yàn)和的計算包括: 12字節(jié)的偽首部 + tcp首部 + tcp數(shù)據(jù)
unsigned short checkSum(unsigned short *buffer, unsigned short 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);
cksum += (cksum >> 16);
return (unsigned short )(~cksum);
}
//ip頭部初始化
void initIpHeader(IPHDR *iph, unsigned int saddr, unsigned int daddr)
{
//syn 包只占用一個標(biāo)記,不占用實(shí)際數(shù)據(jù),因此報文中真實(shí)數(shù)據(jù)也就只是tcp首部
int len = sizeof(IPHDR) + sizeof(TCPHDR);
iph->ver_and_hdrLen = (4 << 4 | sizeof(IPHDR) / sizeof(unsigned int));
iph->tos = 0;
iph->tot_len = htons(len);
iph->id = htons(1);
//iph->frag_off = 0x40;
iph->frag_off = htons(IP_DF);
iph->ttl = 255;
iph->check = 0;
iph->protocol = IPPROTO_TCP;
iph->saddr = saddr;
iph->daddr = daddr;
}
//tcp首部初始化
void initTcpHeader(TCPHDR *tcph, unsigned short sport, unsigned short dport)
{
tcph->sport = htons(sport);
tcph->dport = htons(dport);
tcph->seq = htonl(rand() % 90000000 + 1234 );
tcph->ack_seq = 0;
//長度占4位
tcph->len = (sizeof(TCPHDR)/4 << 4 | 0);
//設(shè)置syn標(biāo)記,其占第二個bit位
tcph->flag = 0x02;
tcph->window = htons(1024);
tcph->check = 0;
tcph->urg_ptr = 0;
}
//初始化tcp偽首部
void initPseudoHeader(PSEUDOHDR *phdr, unsigned int srcaddr, unsigned int dstaddr)
{
phdr->zeros = 0;
phdr->protocol = IPPROTO_TCP;
phdr->length = htons(sizeof(struct tcphdr));
phdr->saddr = srcaddr;
phdr->daddr = dstaddr;
}
//構(gòu)建 SYN 包
int createSynPacket(char *packet, int pkt_len, unsigned int saddr, unsigned short sport,
unsigned int daddr, unsigned short dport)
{
char buf[512] = {0};
int len = 0;
IPHDR iph; // IP 頭部
TCPHDR tcph; // TCP 頭部
PSEUDOHDR pseudoh; // TCP 偽頭部
memset(&iph, 0, sizeof(iph));
memset(&tcph, 0, sizeof(tcph));
memset(&pseudoh, 0, sizeof(pseudoh));
len = sizeof(iph) + sizeof(tcph);
// 初始化頭部信息
initIpHeader(&iph, saddr, daddr);
initTcpHeader(&tcph, sport, dport);
initPseudoHeader(&pseudoh, saddr, daddr);
//計算IP校驗(yàn)和
iph.check = checkSum((u_short *)&iph, sizeof(iph));
// 計算TCP校驗(yàn)和
memcpy(buf , &pseudoh, sizeof(pseudoh)); // 復(fù)制TCP偽頭部
memcpy(buf + sizeof(pseudoh), &tcph, sizeof(tcph)); // 復(fù)制TCP頭部
tcph.check = checkSum((u_short *)buf, sizeof(pseudoh) + sizeof(tcph));
memset(packet, 0, pkt_len);
memcpy(packet, &iph, sizeof(iph));
memcpy(packet + sizeof(iph), &tcph, sizeof(tcph));
return len;
}
//創(chuàng)建原始套接字
int createRawSocket(unsigned int saddr, unsigned short sport)
{
int fd;
int on = 1;
struct sockaddr_in addr;
// 創(chuàng)建一個原始套接字, 指定其關(guān)注TCP協(xié)議
fd = socket(AF_INET, SOCK_RAW, IPPROTO_TCP);
if (fd == -1) {
return -1;
}
addr.sin_family = AF_INET;
addr.sin_port = htons(sport);
addr.sin_addr.s_addr = saddr;
/// 綁定
int ret = bind(fd, (struct sockaddr*)&addr, sizeof(addr));
if(-1 == ret)
{
printf("bind error\\n");
return -1;
}
// 設(shè)置需要手動構(gòu)建IP頭部
if (setsockopt(fd, IPPROTO_IP, IP_HDRINCL, (char *)&on, sizeof(on)) < 0) {
close(fd);
return -1;
}
return fd;
}
void analyIPPack(IPHDR *ip)
{
unsigned char *p = (unsigned char*)&ip->saddr;
printf("Source IP : %u.%u.%u.%u\\n", p[0],p[1],p[2],p[3]);
p = (unsigned char*)&ip->daddr;
printf("dest IP : %u.%u.%u.%u\\n", p[0],p[1],p[2],p[3]);
}
void analyTCPPack(TCPHDR *tcp)
{
printf("Source port : %u\\n", ntohs(tcp->sport));
printf("Dest port : %u\\n", ntohs(tcp->dport));
printf("Seq : %u\\n", ntohl(tcp->seq));
printf("Ack seq : %u\\n", ntohl(tcp->ack_seq));
printf("flag : %x\\n", tcp->flag);
return;
}
//發(fā)送SYN包
int sendSynPacket(int sockfd, unsigned int saddr, unsigned short sport,
unsigned int daddr, unsigned short dport)
{
struct sockaddr_in skaddr;
char packet[256] = {0};
int pkt_len = 0;
memset(&skaddr, 0, sizeof(skaddr));
skaddr.sin_family = AF_INET;
skaddr.sin_port = htons(dport);
skaddr.sin_addr.s_addr = daddr;
pkt_len = createSynPacket(packet, 256, saddr, sport, daddr, dport);
printf("send syn packet\\n");
analyIPPack((IPHDR *)packet);
analyTCPPack((TCPHDR *)(packet + sizeof(IPHDR)));
return sendto(sockfd, packet, pkt_len, 0, (struct sockaddr *)&skaddr,
sizeof(struct sockaddr));
}
int main(int argc, char *argv[])
{
unsigned int saddr;
unsigned short sport;
unsigned int daddr;
unsigned short dport;
int sockfd;
char buf[1024] = {0};
int len = 0;
IPHDR * iphd = NULL;
TCPHDR * tcphd = NULL;
if (argc < 5) {
fprintf(stderr, "Usage: syn );
exit(1);
}
saddr = inet_addr(argv[1]); // 獲取源IP
sport = atoi(argv[2]); // 獲取源端口
daddr = inet_addr(argv[3]); // 獲取目的IP
dport = atoi(argv[4]); // 獲取目的端口
sockfd = createRawSocket(saddr, sport); // 創(chuàng)建原始socket
if (sockfd == -1) {
fprintf(stderr, "Failed to make raw socket\\n");
exit(1);
}
if (sendSynPacket(sockfd, saddr, sport, daddr, dport) < 0) { // 發(fā)送SYN包
fprintf(stderr, "Failed to send syn packet\\n");
}
while(1)
{
len = recv(sockfd, buf, sizeof(buf), 0);
if(len == -1)
{
fprintf(stderr, "recv error %d %s\\n", errno, strerror(errno));
return 0;
}
else if(len == 0)
{
continue;
}
else
{
break;
}
}
printf("\\n recv msg len %d \\n", len);
iphd = (IPHDR *)buf;
tcphd = (TCPHDR *)(iphd + 1);
analyIPPack(iphd);
if(iphd->protocol == IPPROTO_TCP)
{
analyTCPPack(tcphd);
}
close(sockfd);
return 0;
}
運(yùn)行結(jié)果:
# ./synack 10.223.12.10 1234 10.223.12.20 4567
send syn packet
Source IP : 10.223.12.10
dest Ip : 10.223.12.20
Source port : 1234
Dest port : 4567
Seq : 4290617
Ack seq : 0
flag : 2
recv msg len 44
Source IP : 10.223.12.20
dest Ip : 10.223.12.10
Source port : 4567
Dest port : 1234
Seq : 1510433077
Ack seq : 4290618
flag : 12
在 10.223.12.20 上抓包結(jié)果
# tcpdump -i eth0 port 4567
...
02:03:04.641657 IP 10.223.12.10.dbm > localhost.localdomain.personal-agent:
Flags [S], seq 4290617, win 1024, length 0
02:03:04.641707 IP localhost.localdomain.personal-agent > 10.223.12.10.dbm:
Flags [S .], seq 1510433077, ack 4290618, win 29200, options [mss 1460], length 0
02:03:04.641812 IP 10.223.12.10.dbm > localhost.localdomain.personal-agent:
Flags [R], seq 4290618 win 0, length 0
從結(jié)果中可以看到 10.223.12.10 在接收到對端回應(yīng)的 syn + ack 后,系統(tǒng)會自動給對端回應(yīng)一個 RST 復(fù)位報文,導(dǎo)致二者的鏈路斷開。
為什么系統(tǒng)會自動回復(fù) RST 報文呢?
首先先分析客戶端收到對端回應(yīng)的 syn+ack 會發(fā)生什么。
在網(wǎng)絡(luò)層把數(shù)據(jù)包發(fā)送到傳輸層時,會調(diào)用 ip_local_deliver_finish ,在該函數(shù)中會將報文復(fù)制一份給 RAW 套接口,然后會繼續(xù)往下處理,最終會進(jìn)入到 tcp 的接收函數(shù) tcp_v4_rcv,在該函數(shù)中會進(jìn)行套接字的查找。
int tcp_v4_rcv(struct sk_buff *skb)
{
...
/*從ehash或bhash中根據(jù)地址和端口查找傳輸控制塊。
若在ehash中找到,表示3次握手后已建立起了連接,可以正常通信。
若在bhash中找到,表示已經(jīng)綁定了端口,處于監(jiān)聽狀態(tài)。
若都找不到,說明對應(yīng)的傳輸控制塊還沒有創(chuàng)建,跳轉(zhuǎn)*/
sk = __inet_lookup(&tcp_hashinfo, skb->nh.iph->saddr, th->source,
skb->nh.iph->daddr, th->dest,
inet_iif(skb));
if (!sk)
goto no_tcp_socket;
...
//處理沒有創(chuàng)建傳輸控制塊收到的報文情況,通常給對方發(fā)送RST段
no_tcp_socket:
//查找IPSec策略數(shù)據(jù)庫,查找失敗跳轉(zhuǎn)
if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb))
goto discard_it;
/* 檢測報文的長度和校驗(yàn)和,若有異常說明報文已損壞,統(tǒng)計后丟棄,
否則給對端發(fā)送rst段后丟棄*/
if (skb->len < (th->doff << 2) || tcp_checksum_complete(skb)) {
bad_packet:
TCP_INC_STATS_BH(TCP_MIB_INERRS);
} else {
tcp_v4_send_reset(NULL, skb);
}
// 丟棄數(shù)據(jù)包
discard_it:
/* Discard frame. */
kfree_skb(skb);
return 0;
discard_and_relse:
sock_put(sk);
goto discard_it;
//處理傳輸層控制塊處于TIME_WAIT狀態(tài)的情況
do_time_wait:
...
}
從上述代碼可知,當(dāng)客戶端接收到報文后,先調(diào)用 __inet_lookup 查找傳輸控制塊,由于原始套接字沒有創(chuàng)建傳輸控制塊,因此會跳到 no_tcp_socket 處發(fā)送 RST 報文給對端。
在 TCP 中,對于客戶端來講,當(dāng)客戶端調(diào)用 connect 時,會在 hash 表tcp_hashinfo 中加入傳輸控制塊,以便后續(xù)接收報文中能夠找到對應(yīng)的套接字。
tcp_v4_connect
-> inet_hash_connect
-> __inet_check_established
對于原始套接字,其沒有端口號的概念,因此也就不會再hash表中存放傳輸控制塊,收到報文也就找不到對應(yīng)的socket,所以收到 SYN 報文后,系統(tǒng)會自動給對端恢復(fù) RST 復(fù)位報文。
審核編輯:劉清
-
SYN
+關(guān)注
關(guān)注
0文章
9瀏覽量
8258 -
RST
+關(guān)注
關(guān)注
0文章
31瀏覽量
7539 -
RAW
+關(guān)注
關(guān)注
0文章
21瀏覽量
3980
發(fā)布評論請先 登錄
w5500 作為tcp server,客戶端異常發(fā)送【RST,ACK】斷開連接問題
轉(zhuǎn)載:基于labview的自動回復(fù)程序
CANopen SDO通訊 客戶端總是回復(fù)中斷傳輸報文
14-TCP 協(xié)議(連接異常與RST)
使用AT組件+SIM7600如何處理HTTP報文呢
ubuntu RS485 編程 出現(xiàn)回復(fù)報文的數(shù)據(jù)混亂
請教大神AT組件如何處理去http報文的呢?
工業(yè)自動化系統(tǒng)制造報文規(guī)范:第4部分?jǐn)?shù)值控制用伴同標(biāo)準(zhǔn)

基于FPGA深度報文檢測系統(tǒng)

基于離散序列報文的自動提取算法
關(guān)于tcp協(xié)議棧中rst報文的seq跳變問題

區(qū)域短報文和全球短報文服務(wù)的區(qū)別在哪里
應(yīng)用筆記 | TSMaster如何代碼自動發(fā)送LIN報文

評論